﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms.VisualStyles;
using System.Drawing;
using System.Windows.Forms;
using Lib_CommonForm;
using Lib_Common;

public class DataGridHandle : IDisposable
{
	//*******************************************************************
	// Purpose:  Class dùng handle cho Data Grid View
	// Notes:    
	// History:
	//     [Programer]       [DD-MMM-YYYY]       [Action]
	//     LuanNH            10-Aug-2007         Created
	//*******************************************************************  

	private DataGridView withEventsField_m_objGrid;
	private DataGridView m_objGrid {
		get { return withEventsField_m_objGrid; }
		set {
			if (withEventsField_m_objGrid != null) {
				withEventsField_m_objGrid.AllowUserToDeleteRowsChanged -= m_objGrid_AllowUserToDeleteRowsChanged;
				withEventsField_m_objGrid.CellBeginEdit -= m_objGrid_CellBeginEdit;
				withEventsField_m_objGrid.CellEndEdit -= m_objGrid_CellEndEdit;
				withEventsField_m_objGrid.CellMouseClick -= m_objGrid_CellMouseClick;
				withEventsField_m_objGrid.CellStateChanged -= m_objGrid_CellStateChanged;
				withEventsField_m_objGrid.CellValidating -= m_objGrid_CellValidating;
				withEventsField_m_objGrid.ColumnHeaderMouseClick -= m_objGrid_ColumnHeaderMouseClick;
				withEventsField_m_objGrid.DataError -= m_objGrid_DataError;
				withEventsField_m_objGrid.DataSourceChanged -= m_objGrid_DataSourceChanged;
				withEventsField_m_objGrid.KeyDown -= m_objGrid_KeyDown;
				withEventsField_m_objGrid.ColumnAdded -= m_objGrid_ColumnAdded;
				withEventsField_m_objGrid.ColumnRemoved -= m_objGrid_ColumnRemoved;
				withEventsField_m_objGrid.ColumnStateChanged -= m_objGrid_ColumnStateChanged;
				withEventsField_m_objGrid.Paint -= m_objGrid_Paint;
				withEventsField_m_objGrid.CellPainting -= m_objGrid_CellPainting;
				withEventsField_m_objGrid.ColumnWidthChanged -= m_objGrid_ColumnWidthChanged;
				withEventsField_m_objGrid.RowHeaderMouseClick -= m_objGrid_RowHeaderMouseClick;
				withEventsField_m_objGrid.RowsAdded -= m_objGrid_RowsAdded;
				withEventsField_m_objGrid.RowValidated -= m_objGrid_RowValidated;
				withEventsField_m_objGrid.RowValidating -= m_objGrid_RowValidating;
				withEventsField_m_objGrid.UserDeletingRow -= m_objGrid_UserDeletingRow;
				withEventsField_m_objGrid.ReadOnlyChanged -= m_objGrid_ReadOnlyChanged;
				withEventsField_m_objGrid.CellErrorTextChanged -= m_objGrid_CellErrorTextChanged;
				withEventsField_m_objGrid.RowErrorTextChanged -= m_objGrid_RowErrorTextChanged;
				withEventsField_m_objGrid.EditingControlShowing -= m_objGrid_EditingControlShowing;
				withEventsField_m_objGrid.CellValueChanged -= m_objGrid_CellValueChanged;
				withEventsField_m_objGrid.CellDoubleClick -= m_objGrid_CellDoubleClick;
				withEventsField_m_objGrid.SelectionChanged -= m_objGrid_SelectionChanged;
				withEventsField_m_objGrid.CellFormatting -= m_objGrid_CellFormatting;
				withEventsField_m_objGrid.CellContentClick -= m_objGrid_CellContentClick;
				withEventsField_m_objGrid.CellEnter -= m_objGrid_CellEnter;
				withEventsField_m_objGrid.CellMouseLeave -= m_objGrid_CellMouseLeave;
				withEventsField_m_objGrid.PreviewKeyDown -= m_objGrid_PreviewKeyDown;
				withEventsField_m_objGrid.RowEnter -= m_objGrid_RowEnter;
			}
			withEventsField_m_objGrid = value;
			if (withEventsField_m_objGrid != null) {
				withEventsField_m_objGrid.AllowUserToDeleteRowsChanged += m_objGrid_AllowUserToDeleteRowsChanged;
				withEventsField_m_objGrid.CellBeginEdit += m_objGrid_CellBeginEdit;
				withEventsField_m_objGrid.CellEndEdit += m_objGrid_CellEndEdit;
				withEventsField_m_objGrid.CellMouseClick += m_objGrid_CellMouseClick;
				withEventsField_m_objGrid.CellStateChanged += m_objGrid_CellStateChanged;
				withEventsField_m_objGrid.CellValidating += m_objGrid_CellValidating;
				withEventsField_m_objGrid.ColumnHeaderMouseClick += m_objGrid_ColumnHeaderMouseClick;
				withEventsField_m_objGrid.DataError += m_objGrid_DataError;
				withEventsField_m_objGrid.DataSourceChanged += m_objGrid_DataSourceChanged;
				withEventsField_m_objGrid.KeyDown += m_objGrid_KeyDown;
				withEventsField_m_objGrid.ColumnAdded += m_objGrid_ColumnAdded;
				withEventsField_m_objGrid.ColumnRemoved += m_objGrid_ColumnRemoved;
				withEventsField_m_objGrid.ColumnStateChanged += m_objGrid_ColumnStateChanged;
				withEventsField_m_objGrid.Paint += m_objGrid_Paint;
				withEventsField_m_objGrid.CellPainting += m_objGrid_CellPainting;
				withEventsField_m_objGrid.ColumnWidthChanged += m_objGrid_ColumnWidthChanged;
				withEventsField_m_objGrid.RowHeaderMouseClick += m_objGrid_RowHeaderMouseClick;
				withEventsField_m_objGrid.RowsAdded += m_objGrid_RowsAdded;
				withEventsField_m_objGrid.RowValidated += m_objGrid_RowValidated;
				withEventsField_m_objGrid.RowValidating += m_objGrid_RowValidating;
				withEventsField_m_objGrid.UserDeletingRow += m_objGrid_UserDeletingRow;
				withEventsField_m_objGrid.ReadOnlyChanged += m_objGrid_ReadOnlyChanged;
				withEventsField_m_objGrid.CellErrorTextChanged += m_objGrid_CellErrorTextChanged;
				withEventsField_m_objGrid.RowErrorTextChanged += m_objGrid_RowErrorTextChanged;
				withEventsField_m_objGrid.EditingControlShowing += m_objGrid_EditingControlShowing;
				withEventsField_m_objGrid.CellValueChanged += m_objGrid_CellValueChanged;
				withEventsField_m_objGrid.CellDoubleClick += m_objGrid_CellDoubleClick;
				withEventsField_m_objGrid.SelectionChanged += m_objGrid_SelectionChanged;
				withEventsField_m_objGrid.CellFormatting += m_objGrid_CellFormatting;
				withEventsField_m_objGrid.CellContentClick += m_objGrid_CellContentClick;
				withEventsField_m_objGrid.CellEnter += m_objGrid_CellEnter;
				withEventsField_m_objGrid.CellMouseLeave += m_objGrid_CellMouseLeave;
				withEventsField_m_objGrid.PreviewKeyDown += m_objGrid_PreviewKeyDown;
				withEventsField_m_objGrid.RowEnter += m_objGrid_RowEnter;
			}
		}
	}
	private TextBox withEventsField_m_txtCellEdit;
	private TextBox m_txtCellEdit {
		get { return withEventsField_m_txtCellEdit; }
		set {
			if (withEventsField_m_txtCellEdit != null) {
				withEventsField_m_txtCellEdit.Leave -= m_txtCellEdit_Leave;
				withEventsField_m_txtCellEdit.KeyDown -= m_txtCellEdit_KeyDown;
			}
			withEventsField_m_txtCellEdit = value;
			if (withEventsField_m_txtCellEdit != null) {
				withEventsField_m_txtCellEdit.Leave += m_txtCellEdit_Leave;
				withEventsField_m_txtCellEdit.KeyDown += m_txtCellEdit_KeyDown;
			}
		}
	}
	private ContextMenuStrip mnuContext;
	private FormBase m_Form;
	//=============
	private string _sPrimaryKey;
	//=============
		// store list of merge column
	private List<MergeColumnHeaderInfo> m_dicMergeInfo;
	//=============
		//store list of mapping column for paste data
	private Dictionary<string, string> m_dicMappingColumn;
		//store list of mapping value for paste data
	private Dictionary<string, string> m_dicMappingValue;
	//=============
		// store list of required column
	private List<string> m_dicRequiredColumn;
		// store list of filterable column
	private Dictionary<string, string> m_dicFilterColumn;
		// store list of column format by value
	private Dictionary<string, Dictionary<object, string>> m_dicFormatColumn;
	//=============
	private DataTable m_SourceTable;
	private bool m_bIsLoading;
	private int m_iOrder;
	//=============
	private bool _PasteClipboard;
	private bool _MultiDelete;
	private bool _ConfirmDelete;
	private int _RowSelectCount;
	//=============
	private bool _AutoRequired;
	private bool _AllowFilter;
	private bool _KeepMergeHeader;
	//=============
	private MethodInvoker m_DbClickMethod;
	private MethodInvoker m_SelectMethod;
	//=============
	//Sự kiện kiểm tra dữ liệu paste
	public event Row_PasteVerifyEventHandler Row_PasteVerify;
	public delegate void Row_PasteVerifyEventHandler(DataRow drData, ref bool bPassed);
	//Sự kiện thông báo quá trình paste
	public event Row_PasteProgressEventHandler Row_PasteProgress;
	public delegate void Row_PasteProgressEventHandler(int rowIndex, int rowCount);
	//Sự kiện sau khi cập nhật dữ liệu cho 1 dòng
	public event Row_ValidatedEventHandler Row_Validated;
	public delegate void Row_ValidatedEventHandler(DataGridViewRow row);
	//Sự kiện kiểm tra khi xóa dòng
	public event Row_DeletingEventHandler Row_Deleting;
	public delegate void Row_DeletingEventHandler(DataGridViewRow row, ref bool cancel);
	//Sự kiện trước khi cập nhật dữ liệu cho 1 cell
	public event Cell_ValidatingEventHandler Cell_Validating;
	public delegate void Cell_ValidatingEventHandler(DataGridViewCell cell, ref string inputValue);
	//Sự kiện sau khi cập nhật dữ liệu cho 1 cell
	public event Cell_ValidatedEventHandler Cell_Validated;
	public delegate void Cell_ValidatedEventHandler(DataGridViewCell cell);
	//Sự kiện sau khi cập nhật dữ liệu cho 1 cell
	public event CellButton_ClickEventHandler CellButton_Click;
	public delegate void CellButton_ClickEventHandler(DataGridViewButtonCell cell);
	//Sự kiện format dữ liệu cho 1 cell
	public event Cell_FormatEventHandler Cell_Format;
	public delegate void Cell_FormatEventHandler(string columnName, DataGridViewCellFormattingEventArgs e);
	//Sự kiện sort manual
	public event Column_SortEventHandler Column_Sort;
	public delegate void Column_SortEventHandler(string columnName, SortOrder e);

	private int GetPosX(int iFromColIndex)
	{
		if (m_objGrid.Visible) {
			return m_objGrid.GetColumnDisplayRectangle(iFromColIndex, true).X + m_objGrid.Left;
		} else {
			int iX = m_objGrid.RowHeadersWidth + 2;
			for (int i = iFromColIndex - 1; i >= 0; i += -1) {
				if (m_objGrid.Columns[i].Visible) {
					iX += m_objGrid.Columns[i].Width;
				}
			}
			return iX + m_objGrid.Left;
		}
	}

	private int GetPosY(int rowIndex)
	{
		if (m_objGrid.Visible) {
			return m_objGrid.GetRowDisplayRectangle(rowIndex, true).Y + m_objGrid.Top;
		} else {
			int iX = m_objGrid.ColumnHeadersHeight + 2;
			for (int i = rowIndex - 1; i >= 0; i += -1) {
				if (m_objGrid.Rows[i].Visible) {
					iX += m_objGrid.Rows[i].Height;
				}
			}
			return iX + m_objGrid.Top;
		}
	}

	private Point GetCellLocation(DataGridViewCell cell)
	{
		Point controlLoc = m_objGrid.FindForm().PointToClient(m_objGrid.Parent.PointToScreen(m_objGrid.Location));
		if (m_objGrid.Visible) {
			controlLoc.Y += m_objGrid.GetRowDisplayRectangle(cell.RowIndex, true).Y;
			controlLoc.X += m_objGrid.GetColumnDisplayRectangle(cell.ColumnIndex, true).X;
		} else {
			int iY = m_objGrid.ColumnHeadersHeight + 2;
			for (int i = cell.RowIndex - 1; i >= 0; i += -1) {
				if (m_objGrid.Rows[i].Visible) {
					iY += m_objGrid.Rows[i].Height;
				}
			}
			controlLoc.Y += iY;
			//
			int iX = m_objGrid.RowHeadersWidth + 2;
			for (int i = cell.ColumnIndex - 1; i >= 0; i += -1) {
				if (m_objGrid.Columns[i].Visible) {
					iX += m_objGrid.Columns[i].Width;
				}
			}
			controlLoc.X += iX;
		}
		return controlLoc;
	}

	private string GetColumnKey(int columnIndex)
	{
		string retVal = m_objGrid.Columns[columnIndex].DataPropertyName;
		if (string.IsNullOrEmpty(retVal)) {
			retVal = m_objGrid.Columns[columnIndex].Name;
		}
		return retVal;
	}

	public bool ClipboardPaste {
		get {
			if (_PasteClipboard) {
				_PasteClipboard = false;
				return true;
			} else {
				return false;
			}
		}
	}

	public bool AutoRequired {
		get { return _AutoRequired; }
		set {
			_AutoRequired = value;
			if (_AutoRequired) {
				m_dicRequiredColumn = null;
			}
		}
	}

	public bool AllowFilter {
		get { return _AllowFilter; }
		set { _AllowFilter = value; }
	}

	private bool _ShowCheckAll;
	//Public Property ShowCheckAll() As Boolean
	//    Get
	//        Return _ShowCheckAll
	//    End Get
	//    Set(ByVal value As Boolean)
	//        _ShowCheckAll = value
	//    End Set
	//End Property

	public bool KeepMergeHeader {
		get { return _KeepMergeHeader; }
		set {
			_KeepMergeHeader = value;
			if (_KeepMergeHeader) {
				int iHeight = m_objGrid.ColumnHeadersHeight;
				m_objGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
				m_objGrid.ColumnHeadersHeight = iHeight + 18;
			}
		}
	}

	public bool IsValid {
		get {
			string sError = CheckError();
			if (!string.IsNullOrEmpty(sError))
				FormGlobals.Message_Error(sError);
			return string.IsNullOrEmpty(sError);
		}
	}

	public string CheckError()
	{
		if (m_SourceTable == null)
			return "";
		foreach (DataRow dr in m_SourceTable.GetErrors()) {
			if (dr.RowState != DataRowState.Deleted) {
				return "Please correct row(s) error before save data";
			}
		}
		return "";
	}

	private string _SortColumn;
	public string SortColumn {
		get { return _SortColumn; }
		set { _SortColumn = value; }
	}

	private SortOrder _SortDirection;
	public string SortDirection {
		get {
			switch (_SortDirection) {
				case SortOrder.Ascending:
					return "ASC";
				case SortOrder.Descending:
					return "DESC";
				default:
					return "";
			}
		}
		set {
			if (value.ToUpper() == "ASC") {
				_SortDirection = SortOrder.Ascending;
			} else if (value.ToUpper() == "DESC") {
				_SortDirection = SortOrder.Descending;
			} else {
				_SortDirection = SortOrder.None;
			}
		}
	}

	public DataGridHandle(DataGridView objGrid, string sPrimaryKey)
	{
        if(sPrimaryKey==null)
            sPrimaryKey="";
		if (objGrid.FindForm() is FormBase) {
			m_Form =(FormBase) objGrid.FindForm();
		}
		//
		_sPrimaryKey = sPrimaryKey;
		m_objGrid = objGrid;
		//m_objGrid.RowsDefaultCellStyle.BackColor = App_GetColor(CS_GRID_ROW);
		m_objGrid.AlternatingRowsDefaultCellStyle.BackColor =Color.FromArgb(240,240,240); //App_GetColor(CS_GRID_ROW_ALTER);
		m_objGrid.ShowCellErrors = true;
		m_objGrid.ShowRowErrors = true;
		m_objGrid.RowHeadersWidth = 45;
		m_objGrid.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
		m_objGrid.AutoGenerateColumns = false;
		m_objGrid.AllowUserToResizeRows = false;
		//m_objGrid.EnableHeadersVisualStyles = False
		//
		foreach (DataGridViewColumn clm in m_objGrid.Columns) {
			if (clm.ReadOnly) {
				//Grid_SetColumnViewOnly(clm);
            clm.DefaultCellStyle.ForeColor = System.Drawing.Color.Gray     ;       
            clm.DefaultCellStyle.BackColor = Color.LightGray;

			} else {
				clm.DefaultCellStyle.SelectionBackColor = Color.LightBlue;
				clm.DefaultCellStyle.SelectionForeColor = Color.Black;
				//Automatic insert header checkbox
				if (_ShowCheckAll && clm is DataGridViewCheckBoxColumn) {
					CheckBox chk = new CheckBox();
					chk.Size = new Size(0, 0);
					chk.Name = m_objGrid.Name + "_chk" + clm.Name;
					chk.Tag = clm.Name;
					m_objGrid.Controls.Add(chk);
					//
					chk.KeyUp += HeaderCheckBox_KeyUp;
					chk.MouseClick += HeaderCheckBox_MouseClick;
				}
			}
		}
		//
		if (m_objGrid.ColumnHeadersHeight < 36) {
			m_objGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
			m_objGrid.ColumnHeadersHeight = 36;
		}
		//merge
		m_dicMergeInfo = new List<MergeColumnHeaderInfo>();
		//
		if (!m_objGrid.ReadOnly) {
			m_txtCellEdit = new TextBox();
			m_txtCellEdit.Name = "DataGridHandle_txtCellEdit";
			m_txtCellEdit.TextAlign = HorizontalAlignment.Right;
			m_txtCellEdit.BorderStyle = BorderStyle.FixedSingle;
			m_txtCellEdit.Visible = false;
			m_txtCellEdit.TabStop = false;
			m_objGrid.Parent.Controls.Add(m_txtCellEdit);
			//
			if (_AllowFilter) {
				mnuContext = new ContextMenuStrip();
				mnuContext.Items.Add(new ToolStripMenuItem("Edit Filter ...", null, new System.EventHandler(ctxMenu_Click), "Filter_Edit"));
				mnuContext.Items.Add(new ToolStripMenuItem("Clear Filter", null, new System.EventHandler(ctxMenu_Click), "Filter_Clear"));
				//
				m_objGrid.TopLeftHeaderCell.ContextMenuStrip = mnuContext;
			}
		}
		//
        
		FormGlobals.FormControlExt ext = default(FormGlobals.FormControlExt);
		if (!(m_objGrid.Tag is FormGlobals.FormControlExt)) {
			ext = new FormGlobals.FormControlExt();
			ext.HandleObject = this;
			ext.Tag = m_objGrid.Tag;
		} else {
			ext = (FormGlobals.FormControlExt) m_objGrid.Tag;
		}
		ext.HandleObject = this;
		m_objGrid.Tag = ext;
	}

	public void SetPrimaryKey(string sColName)
	{
		_sPrimaryKey = sColName;
	}

	public object GetKeyValue()
	{
		if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.IsNewRow) {
			return null;
		} else {
			return FormGlobals.DB_GetValue((DataRowView)m_objGrid.CurrentRow.Cells[_sPrimaryKey].Value, null);
		}
	}

	public T GetKeyValue<T>()
	{
		return Object_SetValueEx<T>(GetKeyValue());
	}

	public object GetRowValue(string sFieldName)
	{
		if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.IsNewRow) {
			return null;
		} else {
			return FormGlobals.DB_GetValue((DataRowView)m_objGrid.CurrentRow.Cells[sFieldName].Value, null);
		}
	}

	public T GetRowValue<T>(string sFieldName)
	{
		return Object_SetValueEx<T>(GetRowValue(sFieldName));
	}

	public object GetRowValue(int rowIndex, string sFieldName)
	{
		if (m_objGrid.Rows[rowIndex].DataBoundItem == null) {
			return null;
		} else {
			return FormGlobals.DB_GetValue((DataRowView)m_objGrid.Rows[rowIndex].Cells[sFieldName].Value, null);
		}
	}

	public T GetRowValue<T>(int rowIndex, string sFieldName)
	{
		return Object_SetValueEx<T>(GetRowValue(rowIndex, sFieldName));
	}

	public DataRow GetRowData()
	{
		if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.IsNewRow) {
			return null;
		} else {
			return ((DataRowView)m_objGrid.CurrentRow.DataBoundItem).Row;
		}
	}

	public DataRow GetRowData(int rowIndex)
	{
		if (m_objGrid.RowCount > rowIndex) {
			return ((DataRowView)m_objGrid.Rows[rowIndex].DataBoundItem).Row;
		} else {
			return null;
		}
	}

	public void SetMappingColumn(IDataReader dr, string sDataCol, string sOriginCol)
	{
		m_dicMappingColumn = new Dictionary<string, string>();
		while (dr.Read()) {
			m_dicMappingColumn.Add(dr[sDataCol].ToString(), dr[sOriginCol].ToString());
		}
	Globals.Reader_Close(dr);
	}

	public Dictionary<string, string> MappingColumn {
		get { return m_dicMappingColumn; }
		set { m_dicMappingColumn = value; }
	}

	public object SelectedKeyValue {
		get { return GetKeyValue(); }
	}

	public object DataSource {
		get { return m_SourceTable; }
		set { m_objGrid.DataSource = value; }
	}

	public void SetMappingValue(IDataReader dr, string sDataCol, string sOriginCol)
	{
		m_dicMappingValue = new Dictionary<string, string>();
		while (dr.Read()) {
			m_dicMappingValue.Add(dr[sDataCol].ToString(), dr[sOriginCol].ToString());
		}
			Globals.Reader_Close(dr);
	}

	public void Row_DblClick(MethodInvoker DbClickMethod)
	{
		m_DbClickMethod = DbClickMethod;
	}

	public void Row_Selected(MethodInvoker SelectMethod)
	{
		m_SelectMethod = SelectMethod;
	}

	public void Column_MergeHeader(string sMergeHeader, int iFromColIndex, int iColMerge, bool bCover)
	{
		for (int i = 0; i <= iColMerge - 1; i++) {
			m_objGrid.Columns[iFromColIndex + i].Resizable = DataGridViewTriState.False;
			if (!bCover)
				m_objGrid.Columns[iFromColIndex + i].HeaderCell.Style.Alignment = DataGridViewContentAlignment.BottomCenter;
		}
		//
		_mergeCalculate = true;
		//
		MergeColumnHeaderInfo objInfo = null;
		foreach (MergeColumnHeaderInfo objInfo_loopVariable in m_dicMergeInfo) {
			objInfo = objInfo_loopVariable;
			int colIndex = m_objGrid.Columns[objInfo.FromName].Index;
			if (colIndex <= iFromColIndex && colIndex + objInfo.ColSpan > iFromColIndex) {
				objInfo.ColSpan += iColMerge - 1;
				return;
			}
		}
		//
		objInfo = new MergeColumnHeaderInfo();
		objInfo.FromName = m_objGrid.Columns[iFromColIndex].Name;
		objInfo.ColSpan = iColMerge;
		objInfo.Caption = sMergeHeader;
		objInfo.Cover = bCover;
		m_dicMergeInfo.Add(objInfo);
	}

	public void Column_MergeHeader(string sFromColName, int iColMerge)
	{
		Column_MergeHeader("", sFromColName, iColMerge, !_KeepMergeHeader);
	}

	//, ByVal bCover As Boolean)
	public void Column_MergeHeader(string sMergeHeader, int iFromColIndex, int iColMerge)
	{
		Column_MergeHeader(sMergeHeader, iFromColIndex, iColMerge, !_KeepMergeHeader);
	}

	public void Column_MergeHeader(string sMergeHeader, string sFromColName, int iColMerge)
	{
		Column_MergeHeader(sMergeHeader, sFromColName, iColMerge, !_KeepMergeHeader);
	}

	public void Column_MergeHeader(string sMergeHeader, string sFromColName, int iColMerge, bool bCover)
	{
		if (!m_objGrid.Columns.Contains(sFromColName)) {
			throw new Exception(string.Format("MergeColumnHeader: Column name '{0}' not found in data grid column", sFromColName));
		}
		int iIndex = m_objGrid.Columns[sFromColName].Index;
		if (string.IsNullOrEmpty(sMergeHeader))
			sMergeHeader = m_objGrid.Columns[sFromColName].HeaderText;
		Column_MergeHeader(sMergeHeader, iIndex, iColMerge, bCover);
	}

	public void Column_ResetMerge()
	{
		if (m_dicMergeInfo == null)
			return;
		m_dicMergeInfo.Clear();
	}

	public void Column_RepaintMerge()
	{
		if (m_dicMergeInfo != null && m_dicMergeInfo.Count > 0) {
			Merge_Calculate();
			foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo) {
				PaintMergeHeader(objInfo);
			}
		}
	}

	public void Filter_AppendColumn(string sColummName, string sCaption)
	{
		if (m_dicFilterColumn == null) {
			m_dicFilterColumn = new Dictionary<string, string>();
		}
		if (m_dicFilterColumn.ContainsKey(sColummName)) {
			m_dicFilterColumn[sColummName] = sCaption;
		} else {
			m_dicFilterColumn.Add(sColummName, sCaption);
		}
	}

	public void Filter_Reset()
	{
		if ((m_dicFilterColumn != null)) {
			m_dicFilterColumn.Clear();
		}
	}

	//Public Sub Windows.Forms.FormatColumn(ByVal columnName As String, ByVal dicFormat As Dictionary(Of Object, String))
	//    If m_dicFormatColumn Is Nothing Then m_dicFormatColumn = New Dictionary(Of String, Dictionary(Of Object, String))
	//    columnName = columnName.ToUpper
	//    If m_dicFormatColumn.ContainsKey(columnName) Then m_dicFormatColumn.Remove(columnName)
	//    m_dicFormatColumn.Add(columnName, dicFormat)
	//End Sub

	public void Column_Format(string columnName, object formatValue, string colorBackground)
	{
		Column_Format(columnName, formatValue, colorBackground, null);
	}

	public void Column_Format(string columnName, object formatValue, string colorBackground, string colorText)
	{
		if (m_dicFormatColumn == null)
			m_dicFormatColumn = new Dictionary<string, Dictionary<object, string>>();
		columnName = columnName.ToUpper();
		Dictionary<object, string> dicFormat = null;
		if (m_dicFormatColumn.ContainsKey(columnName)) {
			dicFormat = m_dicFormatColumn[columnName];
		} else {
			dicFormat = new Dictionary<object, string>();
			m_dicFormatColumn.Add(columnName, null);
		}
		//
		string formatColor = colorBackground;
		if (!string.IsNullOrEmpty(colorText))
			formatColor += ";" + colorText;
		if (dicFormat.ContainsKey(formatValue))
			dicFormat.Remove(formatValue);
		dicFormat.Add(formatValue, formatColor);
		//
		m_dicFormatColumn[columnName] = dicFormat;
	}

    private Dictionary<string, GridLookupInvoker> m_dicLookupInvoker = new Dictionary<string, GridLookupInvoker>();
	private Dictionary<string, string[]> m_dicLookupInit = new Dictionary<string, string[]>();
	private Dictionary<string, Dictionary<string, string>> m_dicLookupMapping = new Dictionary<string, Dictionary<string, string>>();

	private Dictionary<string, object[]> m_dicLookupCache = new Dictionary<string, object[]>();
	private string GetColumnValue(string[] columnInit)
	{
		string columnValue = null;
		if (columnInit.Length > 2 && !string.IsNullOrEmpty(columnInit[2])) {
			columnValue = columnInit[2];
		} else {
			columnValue = columnInit[0];
		}
		return columnValue;
	}

    public void SetLookup(string[] columnInit, GridLookupInvoker lookupMethod)
	{
		SetLookup(columnInit, null, lookupMethod);
	}

    public void SetLookup(string[] columnInit, string columnMapping, GridLookupInvoker lookupMethod)
	{
		string columnCode = null;
		string columnName = null;
		string columnValue = null;
		columnCode = columnInit[0];
		if (columnInit.Length > 1) {
			columnName = columnInit[1];
			columnValue = GetColumnValue(columnInit);
		}
		//
		m_dicLookupInit.Add(columnCode, columnInit);
		Dictionary<string, string> dicMapping = new Dictionary<string, string>();
		if (!string.IsNullOrEmpty(columnMapping)) {
			foreach (string sMapping in columnMapping.Split(";")) {
				string[] arrColumn = sMapping.Split("=");
				if (arrColumn.Length > 1) {
					dicMapping.Add(arrColumn[0].Trim().ToUpper(), arrColumn[1].Trim());
				}
			}
		}
		m_dicLookupMapping.Add(columnCode, dicMapping);
		//
		DataGridViewColumn clmCode = m_objGrid.Columns(columnCode);
		DataGridViewColumn clmID = default(DataGridViewColumn);
		if (!string.IsNullOrEmpty(columnValue))
			clmID = m_objGrid.Columns(columnValue);
		//
		if (clmCode == null) {
			clmCode = new DataGridViewTextBoxColumn();
			clmCode.Name = columnCode;
			clmCode.DataPropertyName = columnCode;
			clmCode.Width = clmID.Width;
			clmCode.HeaderText = clmID.HeaderText;
			clmCode.HeaderCell = clmID.HeaderCell;
			m_objGrid.Columns.Insert(clmID.Index + 1, clmCode);
		}
		//
		clmCode.DataPropertyName = columnCode;
		clmCode.Visible = true;
		//
		m_dicLookupInvoker.Add(columnCode, lookupMethod);
		//
		if (!(m_objGrid.ReadOnly) && !m_objGrid.Columns.Contains(clmCode.Name + "_clmLookup")) {
			DataGridViewButtonColumn clmButton = NewButtonColumn(clmCode.Name + "_clmLookup", "Lookup data");
			clmButton.Frozen = clmCode.Frozen;
			//
			m_objGrid.Columns.Insert(clmCode.Index + 1, clmButton);
			//
			Column_MergeHeader(clmCode.HeaderText, clmCode.Index, 2, true);
		}
		//
		if (!string.IsNullOrEmpty(columnValue) && columnValue != columnCode) {
			if (!m_objGrid.Columns.Contains(columnValue)) {
				clmID = new DataGridViewTextBoxColumn();
				clmID.Name = columnValue;
				m_objGrid.Columns.Add(clmID);
			} else {
				clmID = m_objGrid.Columns(columnValue);
			}
			clmID.DataPropertyName = columnValue;
			clmID.Visible = false;
			clmID.Width = 0;
		}
		//
		SetCaseInput(columnCode, CharacterCasing.Upper);
	}

	private bool _isLookupInvoke;
    public void Cell_Lookup(DataGridViewCell currentCell, FormGlobals.LookupMode mode)
	{
		_isLookupInvoke = true;
		try {
			string sColumnKey = currentCell.OwningColumn.Name;
			string[] arrInitColumn = m_dicLookupInit[sColumnKey];
			if (mode == FormGlobals.LookupMode.CheckData) {
				//Get data from cache
				if (!string.IsNullOrEmpty(Globals.DB_GetString(currentCell.Value))) {
					object[] arrObject = LookupCacheGet(arrInitColumn, currentCell.OwningRow);
					if ((arrObject != null)) {
						for (int i = 0; i <= arrInitColumn.Length - 1; i++) {
							if (currentCell.DataGridView.Columns.Contains(arrInitColumn[i])) {
								currentCell.OwningRow.Cells[arrInitColumn[i]].Value = arrObject[i];
							}
						}
						currentCell.ErrorText = "";
						//Clear current error check
						if (Cell_Validated != null) {
							Cell_Validated(currentCell);
						}
						return;
					}
				//Clear data
				} else {
					for (int i = 0; i <= arrInitColumn.Length - 1; i++) {
						if (currentCell.DataGridView.Columns.Contains(arrInitColumn[i])) {
							currentCell.OwningRow.Cells[arrInitColumn[i]].Value = DBNull.Value;
						}
					}
					currentCell.ErrorText = "";
					//Clear current error check
					if (Cell_Validated != null) {
						Cell_Validated(currentCell);
					}
					return;
				}
			}
			//Pre invoke
			switch (mode) {
				case LookupMode.SelectData:
					m_objGrid.CurrentCell = currentCell;
					m_objGrid.BeginEdit(false);
					break;
			}
			//Invoke method
			string columnValue = GetColumnValue(arrInitColumn);
			DataRow drEx = m_dicLookupInvoker[sColumnKey].Invoke(currentCell, columnValue, mode);
			//Update data
			if (((drEx != null))) {
				string rowKey = null;
				DataRowView objRowData =(DataRowView) currentCell.OwningRow.DataBoundItem;
				Dictionary<string, string> dicMapping = m_dicLookupMapping[sColumnKey];
				foreach (string dataKey in arrInitColumn) {
					if (string.IsNullOrEmpty(dataKey))
						continue;
					if ((dicMapping != null) && dicMapping.ContainsKey(dataKey.ToUpper())) {
						rowKey = dicMapping[dataKey.ToUpper()];
					} else {
						rowKey = dataKey;
					}
					if (m_objGrid.Columns.Contains(dataKey)) {
						if (m_objGrid.Columns[dataKey] is DataGridViewCheckBoxColumn) {
                            if ((bool)drEx[rowKey])
                            {
								currentCell.OwningRow.Cells[rowKey].Value = "1";
							} else {
								currentCell.OwningRow.Cells[rowKey].Value = "0";
							}
						} else {
							currentCell.OwningRow.Cells[dataKey].Value =Globals. DB_GetNull(drEx[rowKey]);
						}
					} else {
						if (objRowData == null)
							continue;
						if ((m_SourceTable != null) && m_SourceTable.Columns.Contains(rowKey)) {
							if (drEx[rowKey] is bool) {
								if ((bool)drEx[rowKey]) {
									objRowData[rowKey] = "1";
								} else {
									objRowData[rowKey] = "0";
								}
							} else {
                                objRowData[dataKey] = Globals.DB_GetNull(drEx[rowKey]);
							}
						}
					}
				}
				//
				currentCell.ErrorText = "";
				//Clear current error check
				//
				if (Cell_Validated != null) {
					Cell_Validated(currentCell);
				}
			}
			//Post invoke
			switch (mode) {
				case FormGlobals. LookupMode.SelectData:
					if (m_objGrid.EditingControl != null) {
                        m_objGrid.EditingControl.Text = Globals.DB_GetString(currentCell.Value);
						m_objGrid.EndEdit();
					}
					break;
                case FormGlobals.LookupMode.CheckData:
                case FormGlobals.LookupMode.GetData:
					break;
				//Do nothing
			}
			//Store value array to cache
			if (!Globals.IsDBNull(currentCell.OwningRow.Cells[columnValue].Value) && ((drEx != null))) {
				LookupCacheSet(arrInitColumn, currentCell.OwningRow);
			}
		} catch (Exception ex) {
			//Throw ex
            FormGlobals.Message_Error(ex.Message);
		} finally {
			_isLookupInvoke = false;
		}
	}

	private void LookupCacheSet(string[] arrInit, DataGridViewRow row)
	{
		if (Null.IsNull(row.Cells[arrInit[0]].Value))
			return;
		string sCacheKey = arrInit[0] + Constants.vbTab + row.Cells[arrInit[0]].Value;
		if (!m_dicLookupCache.ContainsKey(sCacheKey)) {
			object[] arrObject = Array.CreateInstance(typeof(object), arrInit.Length);
			for (int i = 0; i <= arrInit.Length - 1; i++) {
				if (row.DataGridView.Columns.Contains(arrInit[i])) {
					arrObject[i] = row.Cells(arrInit[i]).Value;
				}
			}
			m_dicLookupCache.Add(sCacheKey, arrObject);
		}
	}

	private object[] LookupCacheGet(string[] arrInit, DataGridViewRow row)
	{
		string sCacheKey = arrInit[0] + Constants.vbTab + row.Cells(arrInit[0]).Value;
		if (m_dicLookupCache.ContainsKey(sCacheKey)) {
			return m_dicLookupCache[sCacheKey];
		} else {
			return null;
		}
	}

	private Dictionary<string, CharacterCasing> m_dicCaseInput = new Dictionary<string, CharacterCasing>();
	public void SetCaseInput(string columnName, CharacterCasing caseInput)
	{
		if (!m_objGrid.Columns.Contains(columnName)) {
			throw new ArgumentException("Column name '" + columnName + "' not found", "columnName");
		}
		if (m_dicCaseInput.ContainsKey(columnName)) {
			m_dicCaseInput[columnName] = caseInput;
		} else {
			m_dicCaseInput.Add(columnName, caseInput);
		}
	}

	public void SetCaseInput(CharacterCasing caseInput, params string[] columnArray)
	{
		foreach (string columnName in columnArray) {
			SetCaseInput(columnName, caseInput);
		}
	}

	private Dictionary<string, int> m_dicMaxInput = new Dictionary<string, int>();
	public void SetMaxInput(string columnName, int maxInput)
	{
		if (!m_objGrid.Columns.Contains(columnName)) {
			throw new ArgumentException("Column name '" + columnName + "' not found", "columnName");
		}
		if (m_SourceTable != null && m_SourceTable.Columns.Contains(columnName)) {
			m_SourceTable.Columns[columnName].MaxLength = maxInput;
		}
		m_dicMaxInput.Add(columnName, maxInput);
	}

	public void SetMaxInput(int maxInput, params string[] columnArray)
	{
		foreach (string columnName in columnArray) {
			SetMaxInput(columnName, maxInput);
		}
	}

		//chứa danh sách cột TimeEx và Date value hoặc Date column
	private Dictionary<string, object> m_dicTimeEx = new Dictionary<string, object>();
		//chứa danh sách cột Date cho TimeEx
	private List<string> m_lstTimeEx = new List<string>();
	public void SetTimeEx(object fieldDate, params string[] columnArray)
	{
		foreach (string columnName in columnArray) {
			if (!m_objGrid.Columns.Contains(columnName)) {
				throw new ArgumentException("Column name '" + columnName + "' not found", "columnArray");
			}
			m_dicTimeEx.Add(columnName, fieldDate);
			if (fieldDate is string) {
				if (!m_lstTimeEx.Contains(fieldDate.ToString()))
					m_lstTimeEx.Add(fieldDate.ToString());
			}
		}
	}

	public void SetCheckAll(bool bShow, params string[] columnArray)
	{
		foreach (string columnName in columnArray) {
			if (!m_objGrid.Columns.Contains(columnName)) {
				throw new ArgumentException("Column name '" + columnName + "' not found", "columnArray");
			} else if (!m_objGrid.Columns(columnName) is DataGridViewCheckBoxColumn) {
				throw new ArgumentException("Column name '" + columnName + "' is not checkbox column", "columnArray");
			}
			CheckBox chk = null;
			if (m_objGrid.Controls.ContainsKey(m_objGrid.Name + "_chk" + columnName))
				chk = m_objGrid.Controls(m_objGrid.Name + "_chk" + columnName);
			if (bShow) {
				if (chk == null) {
					chk = new CheckBox();
					chk.Size = new Size(0, 0);
					chk.Name = m_objGrid.Name + "_chk" + columnName;
					chk.Tag = columnName;
					m_objGrid.Controls.Add(chk);
					//
					chk.KeyUp += HeaderCheckBox_KeyUp;
					chk.MouseClick += HeaderCheckBox_MouseClick;
				}
			} else {
				if (chk == null)
					m_objGrid.Controls.RemoveByKey(m_objGrid.Name + "_chk" + columnName);
			}
		}
	}

	private string m_TableName;
	private List<string> m_lstCellChangeOld;
	private List<string> m_lstCellChangeNew;
	private Timer m_timerChange;
	//
	static internal Int32 CS_CHANGE_INTERVAL = 1500;
	//
	public void SetFormatChange(string tableName)
	{
		m_TableName = tableName;
		if (m_lstCellChangeOld == null)
			m_lstCellChangeOld = new List<string>();
		if (m_lstCellChangeNew == null)
			m_lstCellChangeNew = new List<string>();
		//
		if (m_timerChange == null) {
			m_timerChange = new Timer();
			m_timerChange.Interval = CS_CHANGE_INTERVAL;
			m_timerChange.Tick += m_timerChange_Tick;
			m_timerChange.Start();
			//
			m_bgrOldChange = App_GetColor(CS_CHANGE_COLOR_OLD);
			m_bgrNewChange1 = App_GetColor(CS_CHANGE_COLOR_NEW.Split(";")[0]);
			m_bgrNewChange2 = App_GetColor(CS_CHANGE_COLOR_NEW.Split(";")[1]);
		}
	}
	static internal string CS_CHANGE_COLOR_OLD = "#FF6600";
	static internal string CS_CHANGE_COLOR_NEW = "#FFCC00;#FFFF33";
	private Color m_bgrOldChange;
	private Color m_bgrNewChange;
	private Color m_bgrNewChange1;
	private Color m_bgrNewChange2;
	private void m_timerChange_Tick(object sender, EventArgs e)
	{
		if (m_lstCellChangeNew.Count == 0) {
			return;
		}
		if (m_bgrNewChange == m_bgrNewChange1) {
			m_bgrNewChange = m_bgrNewChange2;
		} else {
			m_bgrNewChange = m_bgrNewChange1;
		}
		foreach (string cellID in m_lstCellChangeNew) {
			Int32 rowIndex = Int32.Parse(cellID.Split('@')[0]);
			Int32 colIndex = Int32.Parse(cellID.Split('@')[1]);
			if (m_objGrid.Rows.Count > rowIndex && m_objGrid.Columns.Count > colIndex) {
				m_objGrid.InvalidateCell(colIndex, rowIndex);
			}
		}
	}
	static internal Int32 CS_CHANGE_NEWLIMIT = 60;
	static internal Int32 CS_CHANGE_OLDLIMIT = 360;
	private void CellChangeInit(DataView dv)
	{
		if (string.IsNullOrEmpty(m_TableName))
			return;
		m_lstCellChangeOld.Clear();
		m_lstCellChangeNew.Clear();
		if (dv == null)
			return;
		string arrKeys = "";
		for (int i = 0; i <= dv.Count - 1; i++) {
			if (arrKeys.Length > 0)
				arrKeys += ",";
			arrKeys += dv[i][_sPrimaryKey].ToString();
		}
		if (arrKeys.Length == 0)
			return;
		DataTable dtChanges = SystemController.Instance().Table_GetChanges(m_TableName, arrKeys);
		DateTime sysdate = SystemController.Instance().Sys_GetDate();
		int iChangeMin = 0;
		for (int i = 0; i <= dv.Count - 1; i++) {
			foreach (DataGridViewColumn clm in m_objGrid.Columns) {
				if (string.IsNullOrEmpty(clm.DataPropertyName))
					continue;
				DataRow dr = DataView_FindRow(dtChanges, string.Format("KEY_VALUE={0} AND FIELD_NAME='{1}'", dv[i][_sPrimaryKey], clm.DataPropertyName));
				if (dr != null) {
					string cellID = string.Format("{0}@{1}", i, clm.Index);
					if (m_lstCellChangeNew.Contains(cellID) || m_lstCellChangeOld.Contains(cellID))
						continue;
					iChangeMin = (sysdate - DB_GetValue<DateTime>(dr["CHANGE_TIME"])).TotalMinutes;
					if (iChangeMin <= CS_CHANGE_NEWLIMIT) {
						m_lstCellChangeNew.Add(cellID);
					} else if (iChangeMin <= CS_CHANGE_OLDLIMIT) {
						m_lstCellChangeOld.Add(cellID);
					}
				}
			}
		}
	}

	private Dictionary<string, object> m_dicMinValue = new Dictionary<string, object>();
	private Dictionary<string, object> m_dicMaxValue = new Dictionary<string, object>();
	public void SetRangeInput(string columnName, object minValue, object maxValue)
	{
		if (m_dicMinValue.ContainsKey(columnName)) {
			m_dicMinValue[columnName] = minValue;
		} else {
			m_dicMinValue.Add(columnName, minValue);
		}
		//
		if (m_dicMaxValue.ContainsKey(columnName)) {
			m_dicMaxValue[columnName] = maxValue;
		} else {
			m_dicMaxValue.Add(columnName, maxValue);
		}
	}

	public void SetRangeInputEx(object minValue, object maxValue, params string[] columnArray)
	{
		foreach (string columnName in columnArray) {
			SetRangeInput(columnName, minValue, maxValue);
		}
	}

	//======================
	// Handle events
	//======================
	private const string cs_clmDeleteRow = "clmDeleteRow";
	private void m_objGrid_AllowUserToDeleteRowsChanged(object sender, System.EventArgs e)
	{
		InitDeleteColumn();
	}

	private void InitDeleteColumn()
	{
		DataGridViewButtonColumn clmDeleteRow = default(DataGridViewButtonColumn);
		if (m_objGrid.Columns.Contains(cs_clmDeleteRow))
			clmDeleteRow = m_objGrid.Columns(cs_clmDeleteRow);
		if (m_objGrid.AllowUserToDeleteRows) {
			if (clmDeleteRow == null) {
				clmDeleteRow = NewButtonColumn(cs_clmDeleteRow, "Delete data");
				m_objGrid.Columns.Add(clmDeleteRow);
			}
		} else {
			if (clmDeleteRow != null)
				clmDeleteRow.Visible = false;
		}
	}

	private DataGridViewButtonColumn NewButtonColumn(string clmName, string sTooltip)
	{
        if(sTooltip==null)
            sTooltip="";
		DataGridViewButtonColumn clmButton = new DataGridViewButtonColumn();
		clmButton.Name = clmName;
		clmButton.Width = 22;
		clmButton.HeaderText = "";
		clmButton.DefaultCellStyle.SelectionBackColor = SystemColors.Control;
		clmButton.DefaultCellStyle.BackColor = SystemColors.Control;
		clmButton.ReadOnly = true;
		clmButton.ToolTipText = sTooltip;
		return clmButton;
	}

	private void HeaderCheckBox_Location(DataGridViewCheckBoxColumn columnCheck)
	{
		string checkName = columnCheck.DataGridView.Name + "_chk" + columnCheck.Name;
		if (columnCheck.DataGridView.Controls.ContainsKey(checkName) && !columnCheck.ReadOnly && !columnCheck.DataGridView.ReadOnly) {
			CheckBox checkControl =(CheckBox) columnCheck.DataGridView.Controls[checkName];
			//
			if (!string.IsNullOrEmpty(columnCheck.HeaderText)) {
				checkControl.Text = columnCheck.HeaderText;
				columnCheck.HeaderText = "";
			}
			//Get the column header cell bounds
			Rectangle oRectangle = columnCheck.DataGridView.GetCellDisplayRectangle(columnCheck.Index, -1, true);
			checkControl.Width = oRectangle.Width - 4;
			checkControl.Height = oRectangle.Height - 4;
			//
			Point oPoint = new Point();
			oPoint.X = oRectangle.Location.X + (oRectangle.Width - checkControl.Width) / 2 + 1;
			oPoint.Y = oRectangle.Location.Y + (oRectangle.Height - checkControl.Height) / 2 + 1;
			//Change the location of the CheckBox to make it stay on the header
			checkControl.Location = oPoint;
			//
			//checkControl.Font = Control_StyleFont(checkControl.Font, CS_GRID_HEADER);
			//checkControl.ForeColor = Control_StyleColor(checkControl.ForeColor, CS_GRID_HEADER);
		}
	}

	private void HeaderCheckBox_MouseClick(object sender, MouseEventArgs e)
	{
		HeaderCheckBox_Click((CheckBox)sender);
	}

	private void HeaderCheckBox_KeyUp(object sender, KeyEventArgs e)
	{
		if (e.KeyCode == Keys.Space) {
			HeaderCheckBox_Click((CheckBox)sender);
		}
	}

	private void HeaderCheckBox_Click(CheckBox ctlCheck)
	{
		object oValue = null;
		foreach (DataGridViewRow Row in m_objGrid.Rows) {
			if (Row.IsNewRow || Row.Cells[ctlCheck.Tag.ToString()].ReadOnly)
				continue;
			if (ctlCheck.Checked) {
				oValue = ((DataGridViewCheckBoxColumn)m_objGrid.Columns[ctlCheck.Tag.ToString()]).TrueValue;
				if (oValue == null)
					oValue = true;
			} else {
				oValue = ((DataGridViewCheckBoxColumn)m_objGrid.Columns[ctlCheck.Tag.ToString()]).FalseValue;
				if (oValue == null)
					oValue = false;
			}
			Row.Cells[ctlCheck.Tag.ToString()].Value = oValue;
		}
		m_objGrid.RefreshEdit();
	}

	private void m_objGrid_CellBeginEdit(object sender, System.Windows.Forms.DataGridViewCellCancelEventArgs e)
	{
		try {
			DataGridViewCell cell = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];
			cell.Tag = "Edit";
			cell.OwningRow.Tag = "Edit";
			//
			if ((m_Form != null))
				m_Form.HandleEsc = true;
		} catch {
		}
	}

	private void m_objGrid_CellEndEdit(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			DataGridViewCell cell = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];
			cell.Tag = null;
			if (m_objGrid.Rows[e.RowIndex].Tag == null && (m_Form != null))
				m_Form.HandleEsc = false;
		} catch {
		}
	}

	private void m_objGrid_CellMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
	{
		try {
			if (m_objGrid.ReadOnly == false && e.Button == MouseButtons.Right && e.RowIndex >= 0 && e.ColumnIndex >= 0) {
				if ((m_objGrid.Columns[e.ColumnIndex].ValueType != null) && "Int32,Int64,Int16,Decimal,String".Contains(m_objGrid.Columns[e.ColumnIndex].ValueType.Name)) {
					if (m_objGrid.SelectedRows.Count > 0)
						return;

					DataGridViewCell mCell = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];

					if (!mCell.Selected || mCell.ReadOnly || !(mCell is DataGridViewTextBoxCell || (mCell is DataGridViewComboBoxCell & m_objGrid.Columns[e.ColumnIndex].ValueType.Name == "String")))
						return;

					Rectangle r = m_objGrid.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
					m_txtCellEdit.Parent = m_objGrid.Parent;
					m_txtCellEdit.Location = new Point(r.X + m_objGrid.Left, r.Y + +m_objGrid.Top);
					m_txtCellEdit.Size = new Size(r.Width, r.Height);
					m_txtCellEdit.Text =Globals. Object_GetDisplayValue(mCell.Value);
					if (mCell.OwningColumn is DataGridViewTextBoxColumn) {
						m_txtCellEdit.MaxLength = ((DataGridViewTextBoxColumn)mCell.OwningColumn).MaxInputLength;
					} else {
						m_txtCellEdit.MaxLength = 0;
					}
					m_txtCellEdit.Tag = m_objGrid.Columns[e.ColumnIndex].ValueType.Name;
					if (m_txtCellEdit.Tag == "String") {
						m_txtCellEdit.TextAlign = HorizontalAlignment.Left;
					} else {
						m_txtCellEdit.TextAlign = HorizontalAlignment.Right;
					}
					m_txtCellEdit.Visible = true;
					m_txtCellEdit.BringToFront();
					//
					m_txtCellEdit.Focus();
					m_txtCellEdit.SelectAll();
					m_objGrid.Enabled = false;
					//m_objGrid.Tag = Nothing
					if ((m_Form != null))
						m_Form.HandleEsc = true;
				}
			}
		} catch (Exception ex) {
		FormGlobals.	Message_Error(ex+ "\nDataGridHandle.CellMouseClick");
		}
	}

	private void m_objGrid_CellStateChanged(object sender, System.Windows.Forms.DataGridViewCellStateChangedEventArgs e)
	{
		try {
			if (m_bIsLoading || m_objGrid.SelectionMode != DataGridViewSelectionMode.CellSelect)
				return;

			if (e.StateChanged == DataGridViewElementStates.Selected && e.Cell.Selected) {
				//If m_objGrid.GetCellCount(DataGridViewElementStates.Selected) <= 1 Then Return
				if (m_objGrid.SelectedCells.Count <= 1)
					return;
				//If m_objGrid.GetCellCount(DataGridViewElementStates.Selected) = 2 Then
				if (m_objGrid.SelectedCells.Count == 2) {
					if (m_objGrid.SelectedCells[0].ColumnIndex != m_objGrid.SelectedCells[1].ColumnIndex) {
						e.Cell.Selected = false;
					}
					return;
				}
				if (m_objGrid.SelectedCells[2].ColumnIndex == m_objGrid.SelectedCells[1].ColumnIndex) {
					if (e.Cell.ColumnIndex != m_objGrid.SelectedCells[2].ColumnIndex) {
						e.Cell.Selected = false;
					}
				} else {
					e.Cell.Selected = false;
				}
			}
		} catch (Exception ex) {
		FormGlobals.	Message_Error(ex +"\nDataGridHandle.CellStateChanged");
		}
	}

	private void m_objGrid_CellValidating(object sender, System.Windows.Forms.DataGridViewCellValidatingEventArgs e)
	{
		try {
			DataGridViewCell cellCurrent = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];
			if (m_objGrid.EditingControl == null || cellCurrent.Tag == null)
				return;
			if (m_objGrid.Columns[e.ColumnIndex] is DataGridViewTextBoxColumn) {
				//
				string inputValue = m_objGrid.EditingControl.Text;
				if (Cell_Validating != null) {
					Cell_Validating(cellCurrent,ref inputValue);
				}
				m_objGrid.EditingControl.Text = inputValue;
				//
				switch (cellCurrent.ValueType.Name) {
					case "DateTime":
						string sValue = m_objGrid.EditingControl.Text;
						if ((cellCurrent.OwningColumn.HeaderCell.Tag != null) && cellCurrent.OwningColumn.HeaderCell.Tag.ToString.ToUpper == "DATESHORT") {
							m_objGrid.CancelEdit();
							ValidateDateShort(sValue, e.ColumnIndex, e.RowIndex);
							return;
						}
						System.DateTime mDate = default(System.DateTime);
						bool isValid = false;
						switch (cellCurrent.OwningColumn.DefaultCellStyle.Format) {
							case Globals.CS_DISPLAY_DATE_FORMAT:
								Globals.Date_FixString(ref sValue,false);
								isValid = Globals.Date_TryParseEx(sValue,ref  mDate);
								if (isValid) {
									m_objGrid.EditingControl.Text = mDate.ToString(Globals.CS_EDIT_DATE_FORMAT);
									cellCurrent.Value = mDate;
									return;
								}
								break;
							default:
								if (m_dicTimeEx.ContainsKey(cellCurrent.OwningColumn.Name)) {
									object rootDate = m_dicTimeEx[cellCurrent.OwningColumn.Name];
									if (rootDate is string)
										rootDate = GetRowValue(e.RowIndex, rootDate.ToString());
									if (!(rootDate is System.DateTime))
										rootDate = DateTime.Today;
									mDate = Globals.Time_ParseEx((DateTime)rootDate, sValue);
									isValid = !Null.IsNull(mDate);
									if (isValid) {
										m_objGrid.CancelEdit();
										cellCurrent.Value = mDate;
										return;
									}
								} else {
									if (cellCurrent.OwningColumn.DefaultCellStyle.Format ==Globals. CS_DISPLAY_TIME_FORMAT) {
										if (Globals.IsDBNull(cellCurrent.Value)) {
											mDate = DateTime.Today;
										} else {
											mDate = (DateTime)cellCurrent.Value;
										}
										mDate = Globals.Time_ParseEx(mDate, sValue);
										isValid = !Null.IsNull(mDate);
										if (isValid) {
											m_objGrid.EditingControl.Text = mDate.ToString(Globals.CS_EDIT_TIME_FORMAT);
											cellCurrent.Value = mDate;
											return;
										}
									}
								}
								break;
						}
						//
						if (!isValid) {
							m_objGrid.EditingControl.Text = "";
							cellCurrent.Value = DBNull.Value;
						}
						break;
					case "Double":
					case "Decimal":
					case "Integer":
					case "Long":
					case "Int32":
					case "Int64":
					case "Int16":
						string sText = m_objGrid.EditingControl.Text;
						if (sText.Length > 1) {
							string sFormula = null;
							decimal iValue = default(decimal);
							if (sText.StartsWith("=")) {
								sFormula = sText.Substring(1);
								if (Globals.CS_DECIMAL_SYMBOL != ".")
									sFormula = sFormula.Replace(Globals.CS_DECIMAL_SYMBOL, ".");
								if ("+-".Contains(sFormula[0].ToString())) {
									Formula_Prepare(sFormula, ref sFormula, ref iValue);
									if (!string.IsNullOrEmpty(sFormula)) {
										Formula_Calculate(cellCurrent, sFormula, iValue);
										m_objGrid.EditingControl.Text = cellCurrent.Value.ToString();
										sFormula = "";
									} else {
										sFormula = sText.Substring(1);
									}
								}
								if (!string.IsNullOrEmpty(sFormula)) {
									iValue = Formula_Evaluate(sFormula);
									if (Null.IsNull(iValue)) {
										m_objGrid.CancelEdit();
									} else {
										cellCurrent.Value = iValue;
										m_objGrid.EditingControl.Text = cellCurrent.Value.ToString();
									}
								}
							} else {
								sFormula = "";
								if (!"+-".Contains(sText[0].ToString()) && Formula_Prepare(sText, ref sFormula, ref iValue)) {
									//Do nothing
								}
								//process formular
								if (!string.IsNullOrEmpty(sFormula)) {
									Formula_Calculate(cellCurrent, sFormula, iValue);
									m_objGrid.EditingControl.Text = cellCurrent.Value.ToString();
								//Correct number data
								} else {
									if (m_SourceTable.Columns.Contains(cellCurrent.OwningColumn.DataPropertyName)) {
										DataColumn columnData = m_SourceTable.Columns[cellCurrent.OwningColumn.DataPropertyName];
										if (columnData.ExtendedProperties.ContainsKey("DATA_TYPE")) {
											iValue =Globals.Object_GetValue<decimal>(sText);
											if (Math.Abs(iValue) > NumericMaxValue("", columnData.ExtendedProperties["DATA_LENGTH"], columnData.ExtendedProperties["DATA_SCALE"])) {
												Message_Warning("Invalid value input, value too large for this data type");
												m_objGrid.CancelEdit();
												//Value too large
											}
										}
									}
								}
							}
						}
						if (cellCurrent.OwningColumn.HeaderCell.Tag == "DateShort") {
							sText = m_objGrid.EditingControl.Text;
							m_objGrid.CancelEdit();
							ValidateDateShort(sText, e.ColumnIndex, e.RowIndex);
						}
						return;
					default:
						return;
				}
				//Clear error data
				DataTable mDataSource = (DataTable)m_objGrid.DataSource;
				string sColumnName = m_objGrid.Columns(e.ColumnIndex).DataPropertyName;
				if (mDataSource.Columns.Contains(sColumnName) && (mDataSource.Columns[sColumnName].DefaultValue != null)) {
					cellCurrent.Value = mDataSource.Columns[sColumnName].DefaultValue;
					m_objGrid.EditingControl.Text = DB_GetString(mDataSource.Columns[sColumnName].DefaultValue);
				} else {
					cellCurrent.Value = DBNull.Value;
					m_objGrid.EditingControl.Text = "";
				}

			} else if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewComboBoxColumn) {
				ComboBox objComboBox = (ComboBox)m_objGrid.EditingControl;
				long iIndex = objComboBox.FindString(e.FormattedValue);
				if (iIndex >= 0) {
					//objComboBox.SelectedIndex = -1
					objComboBox.SelectedIndex = iIndex;
				} else {
					if (objComboBox.Items.Count > 0) {
						objComboBox.SelectedIndex = 0;
					}
				}
			}
		} catch (Exception ex) {
			Message_Error(ex, "DataGridHandle.CellValidating");
		}
	}

	private string ValidateRangeInput(DataGridViewCell cell)
	{
		if (Information.IsDBNull(cell.Value))
			return "";
		string sMessage = "";
		object oValue = cell.Value;
		object oMinValue = null;
		object oMaxValue = null;
		if (m_dicMinValue.ContainsKey(cell.OwningColumn.Name)) {
			oMinValue = m_dicMinValue[cell.OwningColumn.Name];
			if (oMinValue is string && m_objGrid.Columns.Contains(oMinValue.ToString())) {
				oMinValue = DB_GetValue(cell.OwningRow.Cells(oMinValue.ToString()).Value, null);
			}
		}
		if (m_dicMaxValue.ContainsKey(cell.OwningColumn.Name)) {
			oMaxValue = m_dicMaxValue[cell.OwningColumn.Name];
			if (oMaxValue is string && m_objGrid.Columns.Contains(oMaxValue.ToString())) {
				oMaxValue = DB_GetValue(cell.OwningRow.Cells(oMaxValue.ToString()).Value, null);
			}
		}
		//
		bool bReturn = true;
		if (oMinValue != null && oValue < oMinValue)
			bReturn = false;
		if (oMaxValue != null && oValue > oMaxValue)
			bReturn = false;
		//
		if (!bReturn) {
			sMessage = "Invalid value, please input value";
			if (oMinValue != null && oMaxValue != null) {
				sMessage += " from " + Object_GetDisplayValue(oMinValue) + " to " + Object_GetDisplayValue(oMaxValue);
			} else if (oMinValue != null) {
				sMessage += " greate than or equal " + Object_GetDisplayValue(oMinValue);
			} else if (oMaxValue != null) {
				sMessage += " less than or equal " + Object_GetDisplayValue(oMaxValue);
			}
			//
			cell.ErrorText = sMessage;
		//Message_Warning(sMessage)
		//
		//m_objGrid.CancelEdit()
		//cell.Value = DBNull.Value
		//cell.Selected = True
		} else {
			cell.ErrorText = "";
		}
		return sMessage;
	}

	private void ValidateDateShort(string sValue, int iColumnIndex, int iRowIndex)
	{
		string sColName = m_objGrid.Columns(iColumnIndex).Name;
		if (!sColName.Contains("_YEAR")) {
			bool bShortDate = false;
			bShortDate = (sValue.Length < 6 | sValue // ERROR: Unknown binary operator Like
);
			Date_FixString(sValue);

			if (Information.IsDate(sValue)) {
				System.DateTime mDate = default(System.DateTime);
				Object_SetValue(sValue, mDate);
				sValue = mDate.ToString("dd-MMM");
				sColName = m_objGrid.Columns(iColumnIndex).Name + "_YEAR";

				if (!bShortDate) {
					if (m_objGrid.Columns.Contains(sColName)) {
						m_objGrid.Rows(iRowIndex).Cells(sColName).Value = mDate.Year;
					}
				} else {
					if (m_objGrid.EditingControl.Text == "29-FEB") {
					//
					} else {
						string sYear = null;
						if (m_objGrid.Columns.Contains(sColName)) {
							sYear = DB_GetString(m_objGrid.Rows(iRowIndex).Cells(sColName).Value);
						}
						if (string.IsNullOrEmpty(sYear))
							sYear = "1999";
						//"1999"

						Object_SetValue(sValue + "-" + sYear, mDate);
					}
				}
				m_objGrid.Rows(iRowIndex).Cells(iColumnIndex).Value = mDate;
				return;
			} else {
				m_objGrid.Rows(iRowIndex).Cells(iColumnIndex).Value = DBNull.Value;
			}
		} else {
			//Verify
			sColName = sColName.Replace("_YEAR", "");
			string sText = m_objGrid.Rows(iRowIndex).Cells(sColName).EditedFormattedValue;

			System.DateTime mDate = default(System.DateTime);

			Object_SetValue(sText + "-" + sValue, mDate);

			if (Null.IsNull(mDate)) {
				//m_objGrid.EditingControl.Text = ""
				m_objGrid.Rows(iRowIndex).Cells(sColName).Value = DBNull.Value;
				Object_SetValue(sText + "-" + "1999", mDate);
			} else {
				m_objGrid.Rows(iRowIndex).Cells(iColumnIndex).Value = mDate.Year;
			}
			m_objGrid.Rows(iRowIndex).Cells(sColName).Value = mDate;
		}
	}

	private void m_objGrid_ColumnHeaderMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
	{
		try {
			m_bIsLoading = true;
			//Support full column select
			if (m_objGrid.Columns(e.ColumnIndex).SortMode == DataGridViewColumnSortMode.NotSortable) {
				m_objGrid.ClearSelection();
				foreach (DataGridViewRow mRow in m_objGrid.Rows) {
					if (!mRow.IsNewRow) {
						mRow.Cells(e.ColumnIndex).Selected = true;
					}
				}
				return;
			}
			//Support Programmatic sort
			if (m_objGrid.Columns(e.ColumnIndex).SortMode == DataGridViewColumnSortMode.Programmatic) {
				if (_SortColumn != m_objGrid.Columns(e.ColumnIndex).Name) {
					if (!string.IsNullOrEmpty(_SortColumn) && m_objGrid.Columns.Contains(_SortColumn))
						m_objGrid.Columns(_SortColumn).HeaderCell.SortGlyphDirection = SortOrder.None;
					_SortColumn = m_objGrid.Columns(e.ColumnIndex).Name;
					_SortDirection = SortOrder.Ascending;
				} else {
					if (_SortDirection == SortOrder.None) {
						_SortDirection = SortOrder.Ascending;
					} else {
						_SortDirection = 3 - _SortDirection;
					}
				}
				//
				if (Column_Sort != null) {
					Column_Sort(_SortColumn, _SortDirection);
				}
				//
				m_objGrid.Columns(e.ColumnIndex).HeaderCell.SortGlyphDirection = _SortDirection;
			}
		} catch (Exception ex) {
			Message_Error(ex);
		} finally {
			m_bIsLoading = false;
		}
	}

	private void m_objGrid_DataError(object sender, System.Windows.Forms.DataGridViewDataErrorEventArgs e)
	{
		try {
			string sMsg = null;
			if ((e.Exception != null)) {
				sMsg = e.Exception.Message;
			} else {
				return;
			}
			if (e.Context & DataGridViewDataErrorContexts.Commit) {
				if ((m_objGrid.CurrentRow != null) && !m_objGrid.CurrentRow.IsNewRow) {
					if (System.Text.RegularExpressions.Regex.IsMatch(sMsg, "Column '.*' does not allow nulls.")) {
						int iPos = sMsg.IndexOf("'") + 1;
						string sColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
						if (!m_objGrid.Columns.Contains(sColumn)) {
							e.ThrowException = true;
							return;
						}

						int iColIndex = m_objGrid.Columns(sColumn).Index;
						if (m_objGrid.CurrentRow.Cells(iColIndex).Visible) {
							m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells(iColIndex);
						}
						string sColHeader = m_objGrid.Columns(iColIndex).HeaderText;
						if (string.IsNullOrEmpty(sColHeader)) {
							sColHeader = m_objGrid.Columns(iColIndex).ToolTipText;
						}
						sMsg = System.Text.RegularExpressions.Regex.Replace(sMsg, "(Column ').*(' does not allow nulls.)", "$1 " + sColHeader + " $2");
					//m_objGrid.CurrentRow.Cells(iColIndex).ErrorText = sMsg
					} else if (sMsg == "Input string was not in a correct format.") {
						string sDataType = null;
						switch (m_objGrid.Columns(e.ColumnIndex).ValueType.Name) {
							case "Int16":
							case "Int32":
							case "Int64":
							case "Integer":
							case "Long":
							case "Short":
								sDataType = "Integer";
								break;
							case "Float":
							case "Double":
							case "Single":
								sDataType = "Numeric";
								break;
							case "Date":
							case "DateTime":
								sDataType = "Date";
								break;
							default:
								sDataType = m_objGrid.Columns(e.ColumnIndex).ValueType.Name;
								break;
						}
						sMsg = "Please input " + sDataType + " data type";
					//m_objGrid.CurrentCell.ErrorText = sMsg
					} else if (sMsg.IndexOf("The string was not recognized as a valid DateTime") > -1) {
						sMsg = "Please input Date data type";
					//m_objGrid.CurrentCell.ErrorText = sMsg
					} else if (sMsg.Contains("unique")) {
						int iPos = sMsg.IndexOf("'") + 1;
						string arrColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
						string sColHeader = "";
						foreach (string sColumn in arrColumn.Split(",")) {
							if (m_objGrid.Columns.Contains(String_Capitalize(sColumn)))
								sColumn = String_Capitalize(sColumn);
							if (!m_objGrid.Columns.Contains(sColumn))
								return;

							int iColIndex = m_objGrid.Columns(sColumn).Index;
							if (m_objGrid.CurrentRow.Cells(iColIndex).Visible) {
								m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells(iColIndex);
							}
							string tmpHeader = m_objGrid.Columns(iColIndex).HeaderText;
							if (string.IsNullOrEmpty(tmpHeader)) {
								tmpHeader = m_objGrid.Columns(iColIndex).ToolTipText;
							}
							if (sColHeader.Length > 0)
								sColHeader += ", ";
							sColHeader += tmpHeader;
						}
						sMsg = "Please input unique value for column '" + sColHeader + "'";
					} else if (sMsg == "DataGridViewComboBoxCell value is not valid.") {
						sMsg = "Cell value not contains in list";
					} else if (sMsg.IndexOf("MaxLength limit") > -1) {
						int iPos = sMsg.IndexOf("'") + 1;
						string sColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
						if (!m_objGrid.Columns.Contains(sColumn)) {
							e.ThrowException = true;
							return;
						}
						int iColIndex = m_objGrid.Columns(sColumn).Index;
						if (m_objGrid.CurrentRow.Cells(iColIndex).Visible) {
							m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells(iColIndex);
						}
						string sColHeader = m_objGrid.Columns(iColIndex).HeaderText;
						if (string.IsNullOrEmpty(sColHeader)) {
							sColHeader = m_objGrid.Columns(iColIndex).ToolTipText;
						}
						sMsg = "Cannot set column '" + sColHeader + "', the max length input of this column is " + m_SourceTable.Columns[sColumn].MaxLength.ToString() + " character(s)";
					} else if (sMsg.StartsWith("Value was either too")) {
						string sMaxValue = null;
						switch (m_objGrid.Columns(e.ColumnIndex).ValueType.Name) {
							case "Int16":
								sMaxValue = Int16.MaxValue.ToString();
								break;
							case "Int32":
								sMaxValue = Int32.MaxValue.ToString();
								break;
							case "Int64":
								sMaxValue = Int64.MaxValue.ToString();
								break;
							case "Single":
								sMaxValue = float.MaxValue.ToString();
								break;
							case "Double":
								sMaxValue = double.MaxValue.ToString();
								break;
							case "Decimal":
								sMaxValue = decimal.MaxValue.ToString();
								break;
							default:
								sMaxValue = "unknow";
								break;
						}
						sMsg = "Please input value less than or equal " + sMaxValue + "";
					}
					Message_Information(sMsg);
					e.Cancel = true;
					if ((m_Form != null))
						m_Form.HandleEsc = true;
				}
				e.ThrowException = false;
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private bool m_IsLoading;
	private void m_objGrid_DataSourceChanged(object sender, System.EventArgs e)
	{
		m_IsLoading = true;
		try {
			_prevRowIndex = -1;
			if (m_objGrid.DataSource is DataTable) {
				m_SourceTable = m_objGrid.DataSource;
			} else if (m_objGrid.DataSource is DataView) {
				m_SourceTable = ((DataView)m_objGrid.DataSource).Table;
			} else {
				if (m_dicRequiredColumn != null)
					m_dicRequiredColumn.Clear();
				m_dicRequiredColumn = null;
				m_SourceTable = null;
				CellChangeInit(null);
				return;
			}
			//Init change
			CellChangeInit(m_SourceTable.DefaultView);
			//Set key
			if (!string.IsNullOrEmpty(_sPrimaryKey)) {
				DataTable_SetKey(m_SourceTable, _sPrimaryKey);
			}
			//Required
			if (_AutoRequired) {
				foreach (DataColumn mColumn in m_SourceTable.Columns) {
					if (mColumn.AllowDBNull == false && !m_objGrid.Columns.Contains(mColumn.ColumnName)) {
						mColumn.AllowDBNull = true;
					}
				}
			} else {
				//required
				if (m_dicRequiredColumn == null) {
					m_dicRequiredColumn = new List<string>();

					foreach (DataColumn mColumn in m_SourceTable.Columns) {
						if (mColumn.AllowDBNull == false) {
							m_dicRequiredColumn.Add(mColumn.ColumnName.ToUpper());
							mColumn.AllowDBNull = true;
						}
					}
				}
			}
			//
			//For i As Integer = 0 To m_objGrid.Columns.Count - 1
			int i = -1;
			while (i < m_objGrid.Columns.Count - 1) {
				i += 1;
				DataGridViewColumn clm = m_objGrid.Columns(i);
				if ((clm.DataPropertyName != null) && m_SourceTable.Columns.Contains(clm.DataPropertyName)) {
					//(text column only)
					if (clm is DataGridViewTextBoxColumn) {
						if (Type_IsNumeric(m_SourceTable.Columns[clm.DataPropertyName].DataType) && clm is DataGridViewTextBoxColumn) {
							//Auto align number column 
							clm.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
							//Auto correct format by data scale
							if (m_SourceTable.Columns[clm.DataPropertyName].ExtendedProperties.ContainsKey("DATA_SCALE")) {
								clm.DefaultCellStyle.Format = "#,##0." + String_Repeat("#", m_SourceTable.Columns[clm.DataPropertyName].ExtendedProperties["DATA_SCALE"]);
							}
							continue;
						} else if (m_SourceTable.Columns[clm.DataPropertyName].DataType.Name == "DateTime") {
							if (!m_objGrid.ReadOnly && !clm.ReadOnly && clm.DefaultCellStyle.Format == CS_DISPLAY_DATE_FORMAT) {
								//Auto insert button column calendar
								if (!m_objGrid.Columns.Contains(clm.Name + "_clmCalendar")) {
									DataGridViewButtonColumn clmButton = NewButtonColumn(clm.Name + "_clmCalendar");
									m_objGrid.Columns.Insert(clm.Index + 1, clmButton);
									//
									Column_MergeHeader(clm.HeaderText, clm.Index, 2, true);
									//
									i += 1;
								}
							} else {
								//Auto align date column 
								clm.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
							}
						}
					}
				}
			}
			//Next
			//Init lookup
			if (m_dicLookupInvoker.Count > 0) {
				foreach (string sKey in m_dicLookupInvoker.Keys) {
					string[] arrInit = m_dicLookupInit[sKey];
					bool bIsOK = true;
					foreach (object colName_loopVariable in arrInit) {
						colName = colName_loopVariable;
						if (!string.IsNullOrEmpty(colName) && !m_SourceTable.Columns.Contains(colName)) {
							m_SourceTable.Columns.Add(colName);
							bIsOK = false;
						}
					}
					//
					foreach (DataGridViewRow row in m_objGrid.Rows) {
						if (row.IsNewRow)
							continue;
						if (!bIsOK) {
							Cell_Lookup(row.Cells(sKey), LookupMode.GetData);
						} else {
							LookupCacheSet(arrInit, row);
						}
					}
				}
			}
			//Init Delete column
			InitDeleteColumn();
			//Pre select for prevent slow on first selection 
			int x = 0;
			foreach (DataGridViewRow mRow in m_objGrid.Rows) {
				if (mRow.Cells(0).Visible)
					mRow.Cells(0).Selected = true;
				x = +1;
				if (x > 5)
					break; // TODO: might not be correct. Was : Exit For
			}
			m_objGrid.ClearSelection();
			//First row select
			if (m_objGrid.Rows.Count > 0) {
				foreach (DataGridViewCell cell in m_objGrid.Rows(0).Cells) {
					if (cell.Visible) {
						m_objGrid.CurrentCell = cell;
						if (m_objGrid.SelectionMode == DataGridViewSelectionMode.FullRowSelect) {
							cell.OwningRow.Selected = true;
						}
						if (m_objGrid.Visible && m_objGrid.CurrentRow.Index != _prevRowIndex && (m_SelectMethod != null)) {
							_prevRowIndex = m_objGrid.CurrentRow.Index;
							m_SelectMethod.Invoke();
						}
						break; // TODO: might not be correct. Was : Exit For
					}
				}
			// raise event for clear selected info
			} else {
				if ((m_SelectMethod != null))
					m_SelectMethod.Invoke();
			}
			//Column Sort
			if (!string.IsNullOrEmpty(_SortColumn) && m_objGrid.Columns.Contains(_SortColumn)) {
				m_objGrid.Columns(_SortColumn).HeaderCell.SortGlyphDirection = _SortDirection;
			}
		} catch (Exception ex) {
			Message_Error(ex, "DataGridHandle.DataSourceChange");
		} finally {
			m_IsLoading = false;
		}
	}

	private void m_objGrid_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
	{
		try {
			if ((e.Control == true & e.KeyCode == Keys.C) | (e.Control == true & e.KeyCode == Keys.Insert)) {
				if (m_objGrid.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable) {
					m_objGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.Disable;
				}
				//Copy here
				//CopyToClipboard()
				App_ProgressShow(CopyToClipboard, "Copy data to clipboard");
				e.Handled = true;
			} else if ((e.Control == true & e.KeyCode == Keys.V) | (e.Shift == true & e.KeyCode == Keys.Insert)) {
				//Paste here
				if (m_objGrid.ReadOnly)
					return;
				App_ProgressShow(PasteFromClipboard, "Paste data from clipboard");
				e.Handled = true;
			} else if (e.KeyCode == Keys.Delete) {
				if (m_objGrid.ReadOnly || m_objGrid.SelectedRows.Count > 0)
					return;
				//Support delete multiple cells
				foreach (DataGridViewCell mCell in m_objGrid.SelectedCells) {
					if ((m_SourceTable != null) && m_SourceTable.Columns.Contains(mCell.OwningColumn.DataPropertyName) && (!m_SourceTable.Columns[mCell.OwningColumn.DataPropertyName].AllowDBNull || !Information.IsDBNull(m_SourceTable.Columns[mCell.OwningColumn.DataPropertyName].DefaultValue)))
						continue;
					//
					if (!mCell.ReadOnly) {
						mCell.Value = DBNull.Value;
						RaiseEvent_CellEdit(mCell);
					}
				}
			}
		} catch (Exception ex) {
			Message_Error(ex, "DataGridHandle.PreviewKeyDown");
		}
	}

	//Private Sub m_objGrid_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles m_objGrid.PreviewKeyDown

	//End Sub

	private void PasteFromClipboard()
	{
		string sTemp = Clipboard.GetText;
		if (string.IsNullOrEmpty(sTemp) || m_objGrid.ReadOnly) {
			return;
		}
		PasteTextData(sTemp, true);
	}

	private void PasteTextData4Cell(string[] arrRows)
	{
		if (arrRows.Length != m_objGrid.SelectedCells.Count) {
			//Return
		}
		DataGridViewCell mCell = default(DataGridViewCell);
		//
		bool bReverse = false;
		int iLastIndex = 0;
		int iIndex = 0;
		iLastIndex = m_objGrid.SelectedCells.Count - 1;
		//
		bReverse = (m_objGrid.SelectedCells(0).RowIndex > m_objGrid.SelectedCells(iLastIndex).RowIndex);
		//
		int iRowIndex = 0;
		int iCellIndex = 0;
		//Support paste continuous from one selected cell
		if (iLastIndex == 0) {
			iRowIndex = m_objGrid.SelectedCells(0).RowIndex;
			iCellIndex = m_objGrid.SelectedCells(0).ColumnIndex;
		}
		//
		for (int i = 0; i <= arrRows.Length - 1; i++) {
			if (i >= m_objGrid.SelectedCells.Count && iLastIndex > 0)
				return;
			if (bReverse) {
				iIndex = iLastIndex - i;
			} else {
				iIndex = i;
			}
			if (iLastIndex > 0) {
				mCell = m_objGrid.SelectedCells(iIndex);
			} else {
				if (iRowIndex + i == m_objGrid.Rows.Count)
					return;
				//exit 
				mCell = m_objGrid.Rows(iRowIndex + i).Cells(iCellIndex);
			}
			if (mCell.ReadOnly)
				continue;
			if (mCell.OwningRow.IsNewRow) {
				m_objGrid.CurrentCell = mCell;
				m_objGrid.BeginEdit(false);
				m_objGrid.EditingControl.Text = Object_SetValueEx(arrRows[i], mCell.ValueType.Name, true);
				m_objGrid.EndEdit();
			} else {
				mCell.Value = Object_SetValueEx(arrRows[i], mCell.ValueType.Name, true);
			}
			//
			RaiseEvent_CellEdit(mCell);
			//RaiseEvent After_PasteCellData(mCell.RowIndex, mCell.ColumnIndex)
		}
	}


	public void PasteTextData(string sTextData, bool bFromClipboard)
	{
        if (bFromClipboard == null) bFromClipboard = false;
		_PasteClipboard = bFromClipboard;
		sTextData = sTextData.Replace(Constants.vbCrLf, Constants.vbLf);
		string[] arrRows = sTextData.Split(Constants.vbLf);
		//------------------------------------------
		//Check wrong format
		//Data copy must include column name
		//------------------------------------------
		string[] arrHeader = arrRows[0].Split(Constants.vbTab);
		if (arrHeader.Length == 1) {
			PasteTextData4Cell(arrRows);
			return;
		}
		if (bFromClipboard && !m_objGrid.CurrentRow.IsNewRow)
			return;
		if (arrRows.Length == 1)
			return;
		//Replace Column Mapping
		if ((m_dicMappingColumn != null) && m_dicMappingColumn.Count > 0) {
			for (int i = 0; i <= arrHeader.Length - 1; i++) {
				if (m_dicMappingColumn.ContainsKey(arrHeader[i])) {
					arrHeader[i] = m_dicMappingColumn[arrHeader[i]];
				}
			}
		}
		//
		DataTable mDataSource = null;
		if (m_objGrid.DataSource is DataTable) {
			mDataSource = m_objGrid.DataSource;
		} else {
			mDataSource = ((DataView)m_objGrid.DataSource).Table;
		}
		DataTable mDataTemp = mDataSource.Copy();
		mDataTemp.AcceptChanges();
		//
		DataRow mRow = null;
		string sFieldName = null;
		//
		for (int i = 1; i <= arrRows.Length - 1; i++) {
			string[] arrFields = arrRows[i].Split(Constants.vbTab);
			mRow = mDataTemp.NewRow();
			//Continue next row when row field length <> column length
			if (arrFields.Length != arrHeader.Length)
				continue;
			//Handle data error
			try {
				for (int j = 0; j <= arrFields.Length - 1; j++) {
					sFieldName = arrHeader[j];
					if (!mDataTemp.Columns.Contains(sFieldName))
						continue;

					if (sFieldName.ToUpper() == _sPrimaryKey.ToUpper()) {
						mRow[sFieldName] = DBNull.Value;
					} else if (m_objGrid.Columns.Contains(sFieldName) && m_objGrid.Columns(sFieldName).ReadOnly && !Information.IsDBNull(mDataSource.Columns[sFieldName].DefaultValue)) {
						mRow[sFieldName] = mDataSource.Columns[sFieldName].DefaultValue;
					} else {
						if (arrFields[j].Length == 0) {
							if (mDataSource.Columns[sFieldName].AllowDBNull || mDataSource.Columns[sFieldName].DefaultValue == null) {
								mRow[sFieldName] = DBNull.Value;
							} else {
								mRow[sFieldName] = mDataSource.Columns[sFieldName].DefaultValue;
							}
						} else {
							if ((m_dicMappingValue != null) && m_dicMappingValue.ContainsKey(arrFields[j])) {
								arrFields[j] = m_dicMappingValue[arrFields[j]];
							}
							mRow[sFieldName] = arrFields[j];
						}
					}
				}
				mDataTemp.Rows.Add(mRow);
				bool bPassed = true;
				if (Row_PasteVerify != null) {
					Row_PasteVerify(mRow, bPassed);
				}
				if (!bPassed) {
					mDataTemp.Rows.Remove(mRow);
				}
			} catch {
				App_ProgressPause();
				DialogResult rs = Message_YesNoCancel("Some error occur when paste rows data." + Constants.vbCr + "Click Yes to continue with others rows." + Constants.vbCr + "Click No to stop with current rows." + Constants.vbCr + "Click Cancel to cancel paste data.");
				if (rs == DialogResult.Yes) {
					App_ProgressResume();
					continue;
				} else if (rs == DialogResult.No) {
					break; // TODO: might not be correct. Was : Exit For
				} else {
					return;
				}
			}
		}
		//Finish add rows
		int iRowCount = 0;
		mDataTemp = mDataTemp.GetChanges(DataRowState.Added);
		if ((mDataTemp != null))
			iRowCount = mDataTemp.Rows.Count;
		if (iRowCount > 0) {
			int iRowPos = 0;
			//
			string sOldFilter = mDataSource.DefaultView.RowFilter;
			mDataSource.DefaultView.RowFilter = "";
			//
			int rowIndex = 0;
			foreach (DataRow mRow_loopVariable in mDataTemp.Rows) {
				mRow = mRow_loopVariable;
				iRowPos += 1;
				if (Row_PasteProgress != null) {
					Row_PasteProgress(iRowPos, iRowCount);
				}
				mDataSource.ImportRow(mRow);
				if (bFromClipboard) {
					rowIndex = m_objGrid.NewRowIndex - 2;
				} else {
					rowIndex = m_objGrid.NewRowIndex - 1;
				}
				foreach (DataGridViewCell mCell in m_objGrid.Rows(rowIndex).Cells) {
					if (!mCell is DataGridViewButtonCell)
						if (Cell_Validated != null) {
							Cell_Validated(mCell);
						}
					if (!_isLookupInvoke && m_dicLookupInvoker.ContainsKey(m_objGrid.Columns(mCell.ColumnIndex).Name)) {
						Cell_Lookup(m_objGrid.Rows(mCell.RowIndex).Cells(mCell.ColumnIndex), LookupMode.CheckData);
					}
				}
				if (Row_Validated != null) {
					Row_Validated(m_objGrid.Rows(rowIndex));
				}
			}
			//
			if (iRowCount > 0 && bFromClipboard) {
				_deleteIndex = m_objGrid.CurrentRow.Index;
				m_objGrid.Rows.Remove(m_objGrid.CurrentRow);
			}
			//
			mDataSource.DefaultView.RowFilter = sOldFilter;
		}
		//
		if ((mDataTemp != null))
			mDataTemp.Dispose();
	}


	private int _deleteIndex = -1;
	private void CopyToClipboard()
	{
		string sTemp = null;

		if (m_objGrid.SelectedRows.Count == 0) {
			if (m_objGrid.SelectedCells.Count == 0)
				return;
			//For Step=-1 : support right order when copy select multi rows
			//For i As Integer = m_objGrid.SelectedCells.Count - 1 To 0 Step -1
			bool bReverse = false;
			int iLastIndex = 0;
			int iIndex = 0;
			iLastIndex = m_objGrid.SelectedCells.Count - 1;
			//
			bReverse = (m_objGrid.SelectedCells(0).RowIndex > m_objGrid.SelectedCells(iLastIndex).RowIndex);
			sTemp = "";
			//
			for (int i = 0; i <= iLastIndex; i++) {
				if (bReverse) {
					iIndex = iLastIndex - i;
				} else {
					iIndex = i;
				}
				if (sTemp.Length > 0)
					sTemp += Constants.vbCrLf;
				if (m_objGrid.SelectedCells(iIndex).Value is System.DateTime) {
					sTemp += ((System.DateTime)m_objGrid.SelectedCells(iIndex).Value).ToString(CS_DISPLAY_DATETIME_FORMAT);
				} else {
					sTemp += DB_GetString(m_objGrid.SelectedCells(iIndex).Value);
				}
			}
			Clipboard.SetText(sTemp);
			return;
		}
		//Disable copy
		if (m_objGrid.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable)
			m_objGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.Disable;
		DataTable mDataSource = null;
		if (m_objGrid.DataSource is DataTable) {
			mDataSource = m_objGrid.DataSource;
		} else {
			mDataSource = ((DataView)m_objGrid.DataSource).Table;
		}


		DataRowView dtRow = null;
		string sColumn = null;
		string sRow = null;
		//------------------------------------------------
		// Don't Copy column which is
		// - Primary Column
		// - Not contains in Grid column and Column Name <> Column Caption
		// - Grid Column is not visible
		//------------------------------------------------
		foreach (DataColumn mCol in mDataSource.Columns) {
			if (mCol.ColumnName == _sPrimaryKey || (!m_objGrid.Columns.Contains(mCol.ColumnName) && mCol.ColumnName == mCol.Caption) || (m_objGrid.Columns.Contains(mCol.ColumnName) && !m_objGrid.Columns(mCol.ColumnName).Visible)) {
				mCol.AllowDBNull = true;
				continue;
			}
			if (!string.IsNullOrEmpty(sRow)) {
				sRow += Constants.vbTab;
			}
			sRow += mCol.ColumnName;
		}
		string[] arrColumns = sRow.Split(Constants.vbTab);
		sTemp = sRow;

		//For Step=-1 : support right order when copy select multi rows
		for (int i = m_objGrid.SelectedRows.Count - 1; i >= 0; i += -1) {
			DataGridViewRow mRow = m_objGrid.SelectedRows(i);
			if (!(mRow.IsNewRow || mRow.DataBoundItem == null)) {
				dtRow = mRow.DataBoundItem;
				sRow = null;
				foreach (string sColumn_loopVariable in arrColumns) {
					sColumn = sColumn_loopVariable;
					if ((sRow != null)) {
						sRow += Constants.vbTab;
					}
					if (dtRow[sColumn] is System.DateTime) {
						sTemp += ((System.DateTime)dtRow[sColumn]).ToString(CS_DISPLAY_DATETIME_FORMAT);
					} else {
						sRow += DB_GetString(dtRow[sColumn]);
					}
				}
			} else {
				continue;
			}
			sTemp += Constants.vbCrLf + sRow;
		}
		Clipboard.SetText(sTemp);
	}

	private void m_objGrid_ColumnAdded(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
	{
		_mergeCalculate = true;
	}

	//Private Function Grid_LastDisplayIndex() As Integer
	//    Dim retVal As Integer = 0
	//    For Each clm As DataGridViewColumn In m_objGrid.Columns
	//        If retVal < clm.DisplayIndex Then retVal = clm.DisplayIndex
	//    Next
	//    Return retVal
	//End Function

	private void m_objGrid_ColumnRemoved(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
	{
		_mergeCalculate = true;
	}

	private void m_objGrid_ColumnStateChanged(object sender, System.Windows.Forms.DataGridViewColumnStateChangedEventArgs e)
	{
		if (e.StateChanged == DataGridViewElementStates.Visible) {
			_mergeCalculate = true;
			//
			if (m_objGrid.Columns.Contains(e.Column.Name + "_clmLookup")) {
				m_objGrid.Columns(e.Column.Name + "_clmLookup").Visible = e.Column.Visible;
			} else if (m_objGrid.Columns.Contains(e.Column.Name + "_clmCalendar")) {
				m_objGrid.Columns(e.Column.Name + "_clmCalendar").Visible = e.Column.Visible;
			}
		}
	}

	private bool _mergeCalculate;
	private bool _mergeRePaint;
	private string _mergeTheme;
	private List<int> _mergeColumn;
	private void Merge_Calculate()
	{
		if (!_mergeCalculate)
			return;
		_mergeColumn = new List<int>();
		_mergeTheme = App_GetTheme();
		foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo) {
			objInfo.FromIndex = m_objGrid.Columns(objInfo.FromName).Index;
			objInfo.ToIndex = objInfo.FromIndex + objInfo.ColSpan - 1;
			//
			objInfo.Width = -3;
			for (int i = objInfo.FromIndex; i <= objInfo.ToIndex; i++) {
				if (m_objGrid.Columns(i).Visible)
					objInfo.Width += m_objGrid.Columns(i).Width;
				_mergeColumn.Add(i);
			}
		}
		_mergeCalculate = false;
	}

	private void m_objGrid_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
	{
		Column_RepaintMerge();
	}

	private MergeColumnHeaderInfo GetMergeInfo(int columnIndex)
	{
		foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo) {
			if (objInfo.FromIndex <= columnIndex && objInfo.ToIndex >= columnIndex) {
				return objInfo;
			}
		}
		return null;
	}

	private void m_objGrid_CellPainting(object sender, System.Windows.Forms.DataGridViewCellPaintingEventArgs e)
	{
		if (e.RowIndex == -1 && e.ColumnIndex > -1) {
			//Paint merge header
			Merge_Calculate();
			MergeColumnHeaderInfo objInfo = GetMergeInfo(e.ColumnIndex);
			//
			StringFormat format = new StringFormat();
			format.Alignment = StringAlignment.Center;
			format.LineAlignment = StringAlignment.Center;
			string sCaption = m_objGrid.Columns(e.ColumnIndex).HeaderText;
			//
			if (objInfo == null) {
				e.PaintBackground(e.CellBounds, true);
				e.Paint(e.CellBounds, DataGridViewPaintParts.ContentBackground);
				e.Graphics.DrawString(m_objGrid.Columns(e.ColumnIndex).HeaderText, Control_StyleFont(e.CellStyle.Font, CS_GRID_HEADER), new SolidBrush(Control_StyleColor(e.CellStyle.ForeColor, CS_GRID_HEADER)), e.CellBounds, format);
				e.Handled = true;
			} else {
				if (!objInfo.Cover) {
					Rectangle r2 = e.CellBounds;
					r2.Height = (r2.Height / 2) - 2;
					r2.Y = r2.Height + 5;
					//
					e.PaintBackground(r2, true);
					e.Graphics.DrawString(sCaption, m_objGrid.ColumnHeadersDefaultCellStyle.Font, new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.ForeColor), r2, format);
					e.Handled = true;
				}
			}
			//Relocation checkbox header
			//If _ShowCheckAll AndAlso TypeOf m_objGrid.Columns(e.ColumnIndex) Is DataGridViewCheckBoxColumn Then
			if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewCheckBoxColumn) {
				HeaderCheckBox_Location(m_objGrid.Columns(e.ColumnIndex));
			}
		} else {
			if (e.ColumnIndex >= 0 && e.RowIndex >= 0) {
				if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewButtonColumn) {
					if (m_objGrid.Columns(e.ColumnIndex).Name.EndsWith("_clmCalendar")) {
						e.Paint(e.CellBounds, DataGridViewPaintParts.All);
						e.Graphics.DrawImage(My.Resources.btnCalendar, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
						e.Handled = true;
					} else if (m_objGrid.Columns(e.ColumnIndex).Name.EndsWith("_clmLookup")) {
						e.Paint(e.CellBounds, DataGridViewPaintParts.All);
						e.Graphics.DrawImage(My.Resources.btnFind, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
						e.Handled = true;
					} else if (m_objGrid.Columns(e.ColumnIndex).Name == cs_clmDeleteRow) {
						e.Paint(e.CellBounds, DataGridViewPaintParts.All);
						e.Graphics.DrawImage(My.Resources.btnDelete, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
						e.Handled = true;
					} else {
						Image btnImage = default(Image);
						btnImage = GetResouceImage(m_objGrid.Columns(e.ColumnIndex).Tag);
						if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewDisableButtonColumn && !((DataGridViewDisableButtonCell)m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex)).Enabled) {
							btnImage = MakeGrayscale(btnImage);
						}
						if (btnImage != null) {
							e.Paint(e.CellBounds, DataGridViewPaintParts.All);
							e.Graphics.DrawImage(btnImage, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
							e.Handled = true;
						}
					}
				} else if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewImageColumn) {
					Image btnImage = GetResouceImage(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString());
					if (btnImage != null) {
						e.Paint(e.CellBounds, DataGridViewPaintParts.All);
						e.Graphics.DrawImage(btnImage, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
						e.Handled = true;
					}
				}
			}
		}
	}

	private Bitmap MakeGrayscale(Bitmap original)
	{
		//create a blank bitmap the same size as original
		Bitmap newBitmap = new Bitmap(original.Width, original.Height);

		//get a graphics object from the new image
		Graphics g = Graphics.FromImage(newBitmap);

		//create the grayscale ColorMatrix
		Imaging.ColorMatrix colorMatrix = new Imaging.ColorMatrix(new float[][] {
			new float[] {
				0.3f,
				0.3f,
				0.3f,
				0,
				0
			},
			new float[] {
				0.59f,
				0.59f,
				0.59f,
				0,
				0
			},
			new float[] {
				0.11f,
				0.11f,
				0.11f,
				0,
				0
			},
			new float[] {
				0,
				0,
				0,
				1,
				0
			},
			new float[] {
				0,
				0,
				0,
				0,
				1
			}
		});

		//create some image attributes
		Imaging.ImageAttributes attributes = new Imaging.ImageAttributes();

		//set the color matrix attribute
		attributes.SetColorMatrix(colorMatrix);

		//draw the original image on the new image
		//using the grayscale color matrix
		g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

		//dispose the Graphics object
		g.Dispose();
		return newBitmap;
	}

	private void m_objGrid_ColumnWidthChanged(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
	{
		try {
			if (!m_objGrid.Visible || m_dicMergeInfo == null)
				return;
			foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo) {
				if (e.Column.DisplayIndex < objInfo.FromIndex || e.Column.DisplayIndex > objInfo.ToIndex)
					continue;
				objInfo.Width = GetColWidth(objInfo.FromIndex, objInfo.ToIndex);
				PaintMergeHeader(objInfo);
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private int GetColWidth(int iFromColIndex, int iToColIndex)
	{
		int iW = 0;
		for (int i = iFromColIndex; i <= iToColIndex; i++) {
			iW += m_objGrid.Columns(i).Width;
		}
		return iW;
	}

	private void PaintMergeHeader(MergeColumnHeaderInfo objInfo)
	{
		Rectangle recHeader = new Rectangle();
		int startIndex = -1;
		for (int colIndex = objInfo.FromIndex; colIndex <= objInfo.ToIndex; colIndex++) {
			Rectangle cellRec = m_objGrid.GetCellDisplayRectangle(colIndex, -1, true);
			if (cellRec.Width > 0) {
				if (startIndex == -1) {
					startIndex = colIndex;
					recHeader.X += cellRec.X;
					recHeader.Height = cellRec.Height;
				}
			}
			if (colIndex <= startIndex || startIndex == -1) {
				if (colIndex < m_objGrid.Columns.Count - 1 && !m_objGrid.GetCellDisplayRectangle(colIndex + 1, -1, true).IsEmpty) {
					recHeader.X -= m_objGrid.Columns(colIndex).Width - cellRec.Width;
				}
			}
		}
		//
		if (startIndex == -1)
			return;
		//
		Rectangle recExclude = new Rectangle(0, 0, 1, m_objGrid.ColumnHeadersHeight);
		if (m_objGrid.RowHeadersVisible)
			recExclude.Width += m_objGrid.RowHeadersWidth;
		for (int colIndex = 0; colIndex <= objInfo.FromIndex - 1; colIndex++) {
			if (m_objGrid.Columns(colIndex).Frozen)
				recExclude.Width += m_objGrid.Columns(colIndex).Width + 1;
		}
		//
		int w2 = objInfo.Width;
		//
		recHeader.X += 1;
		recHeader.Y += 2;
		recHeader.Width = w2;

		if (_mergeTheme == "WindowsClassic") {
			recHeader.Y += 1;
			if (objInfo.Cover)
				recHeader.Height -= 3;
		}

		Graphics objGraphics = default(Graphics);
		StringFormat format = new StringFormat();
		format.Alignment = GetStringAlignment(m_objGrid.Columns(objInfo.FromIndex).HeaderCell.Style.Alignment);
		format.LineAlignment = StringAlignment.Center;
		//
		objGraphics = m_objGrid.CreateGraphics;
		if (recHeader.X < recExclude.Width) {
			objGraphics.ExcludeClip(recExclude);
		}
		if (!objInfo.Cover)
			recHeader.Height = recHeader.Height / 2;
		//Draw background
		if (_mergeTheme == "WindowsClassic") {
			objGraphics.FillRectangle(new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.BackColor), recHeader);
		} else {
			Rectangle rTop = default(Rectangle);
			Rectangle rBottom = default(Rectangle);
			rTop = new Rectangle(recHeader.X, recHeader.Y, recHeader.Width, 0);
			rBottom = rTop;
			//
			rTop.Height = 10;
			rTop.Y += 1;
			//
			rBottom.Y = rTop.Bottom - 1;
			rBottom.Height = recHeader.Height - rTop.Height - 2;
			SolidBrush gbrush = new SolidBrush(App_GetColor("#ffffff"));
			objGraphics.FillRectangle(gbrush, rTop);
			SolidBrush brushMiddle = new SolidBrush(App_GetColor("#f7f8fa"));
			objGraphics.FillRectangle(brushMiddle, rBottom);
		}
		//Draw text
		objGraphics.DrawString(objInfo.Caption, m_objGrid.ColumnHeadersDefaultCellStyle.Font, new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.ForeColor), recHeader, format);
		//Draw line if not cover
		if (!objInfo.Cover) {
			Pen pen1 = default(Pen);
			Pen pen2 = default(Pen);
			if (_mergeTheme == "WindowsClassic") {
				pen1 = Pens.Gray;
				pen2 = Pens.WhiteSmoke;
			} else {
				pen1 = Pens.LightGray;
				pen2 = Pens.White;
			}
			int h = recHeader.Height;
			objGraphics.DrawLine(pen1, recHeader.Left, h - 0, recHeader.Left + w2 - 1, h - 0);
			objGraphics.DrawLine(pen2, recHeader.Left, h + 1, recHeader.Left + w2 - 1, h + 1);
		}
		objGraphics.Dispose();
	}

	private object GetStringAlignment(DataGridViewContentAlignment contentAlign)
	{
		switch (contentAlign) {
			case DataGridViewContentAlignment.MiddleRight:
			case DataGridViewContentAlignment.BottomRight:
			case DataGridViewContentAlignment.TopRight:
				return StringAlignment.Far;
			case DataGridViewContentAlignment.MiddleCenter:
			case DataGridViewContentAlignment.BottomCenter:
			case DataGridViewContentAlignment.TopCenter:
				return StringAlignment.Center;
			case DataGridViewContentAlignment.NotSet:
				return StringAlignment.Center;
			default:
				return StringAlignment.Near;
		}
	}

	private void m_objGrid_RowHeaderMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
	{
		try {
			m_objGrid.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
			m_objGrid.Rows(e.RowIndex).Selected = true;
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_RowsAdded(object sender, System.Windows.Forms.DataGridViewRowsAddedEventArgs e)
	{
		try {
			foreach (DataGridViewCell objCell in m_objGrid.Rows(e.RowIndex).Cells) {
				if (objCell is DataGridViewButtonCell) {
					objCell.ToolTipText = objCell.OwningColumn.ToolTipText;
					if (objCell is DataGridViewDisableButtonCell && !string.IsNullOrEmpty(objCell.OwningColumn.DataPropertyName)) {
						((DataGridViewDisableButtonCell)objCell).Enabled = GetRowValue<bool>(e.RowIndex, objCell.OwningColumn.DataPropertyName);
					}
				}
			}
			if (_AutoRequired || m_dicRequiredColumn == null || m_SourceTable == null)
				return;
			//Correct default value for required columns
			string mColumnName = null;
			foreach (string mColumnName_loopVariable in m_dicRequiredColumn) {
				mColumnName = mColumnName_loopVariable;
				if (((m_objGrid.Rows(e.RowIndex).DataBoundItem != null)) && m_SourceTable.Columns.Contains(mColumnName) && ((m_SourceTable.Columns[mColumnName].DefaultValue != null)) && !Information.IsDBNull(m_SourceTable.Columns[mColumnName].DefaultValue) && Information.IsDBNull(m_objGrid.Rows(e.RowIndex).DataBoundItem(mColumnName))) {
					m_objGrid.Rows(e.RowIndex).DataBoundItem(mColumnName) = m_SourceTable.Columns[mColumnName].DefaultValue;
				}
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_RowValidated(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		if ((m_objGrid.CurrentRow != null)) {
			if (e.RowIndex == _deleteIndex) {
				_deleteIndex = -1;
				return;
			}
			m_objGrid.CurrentRow.Tag = null;
			if ((m_Form != null))
				m_Form.HandleEsc = false;
		}
	}

	private void m_objGrid_RowValidating(object sender, System.Windows.Forms.DataGridViewCellCancelEventArgs e)
	{
		try {
			if (m_objGrid.Rows(e.RowIndex).Tag == null || m_bIsLoading)
				return;
			if (!m_objGrid.Rows(e.RowIndex).IsNewRow && (m_SourceTable != null) && e.RowIndex >= m_SourceTable.DefaultView.Count) {
				return;
			}
			ValidateRow(e.RowIndex);
			//Prevent unexpected row validated by raise on validating
			if (Row_Validated != null) {
				Row_Validated(m_objGrid.Rows(e.RowIndex));
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void ValidateRow(int iRowIndex)
	{
		m_objGrid.Rows(iRowIndex).ErrorText = "";
		if (m_objGrid.Rows(iRowIndex).IsNewRow || m_SourceTable == null || iRowIndex >= m_SourceTable.DefaultView.Count)
			return;
		DataRow dr = null;
		if (iRowIndex < m_SourceTable.DefaultView.Count && m_objGrid.Rows(iRowIndex).DataBoundItem is DataRowView) {
			dr = ((DataRowView)m_objGrid.Rows(iRowIndex).DataBoundItem).Row;
		}
		if ((dr != null))
			dr.RowError = "";

		string sDataColName = null;
		foreach (DataGridViewCell mCell in m_objGrid.Rows(iRowIndex).Cells) {
			sDataColName = m_objGrid.Columns(mCell.ColumnIndex).DataPropertyName;
			if (mCell is DataGridViewCheckBoxCell) {
				if (Information.IsDBNull(mCell.Value))
					mCell.Value = "0";
				continue;
			} else if (mCell is DataGridViewComboBoxCell) {
				if (!Information.IsDBNull(mCell.Value) && mCell.Value.ToString == mCell.EditedFormattedValue.ToString) {
					if ((dr != null) && !string.IsNullOrEmpty(sDataColName)) {
						dr.SetColumnError(sDataColName, "");
					} else {
						mCell.ErrorText = "";
					}
					continue;
				}
			}
			if ((m_dicRequiredColumn != null) && m_dicRequiredColumn.Contains(mCell.OwningColumn.DataPropertyName.ToUpper)) {
				if (string.IsNullOrEmpty(DB_GetString(mCell.Value))) {
					if (string.IsNullOrEmpty(mCell.ErrorText)) {
						mCell.ErrorText = "Required value";
						if ((dr != null) && !string.IsNullOrEmpty(sDataColName)) {
							dr.SetColumnError(sDataColName, "Required value");
						} else {
							mCell.ErrorText = "Required value";
						}
					}
				} else {
					if (mCell.ErrorText == "Required value") {
						mCell.ErrorText = "";
						if ((dr != null) && !string.IsNullOrEmpty(sDataColName)) {
							dr.SetColumnError(sDataColName, "");
						} else {
							mCell.ErrorText = "";
						}
						continue;
					}
				}
			}
			if (!string.IsNullOrEmpty(mCell.ErrorText)) {
				string sHead = mCell.OwningColumn.HeaderText;
				if ((mCell.OwningColumn.Tag != null))
					sHead = mCell.OwningColumn.Tag;
				if (string.IsNullOrEmpty(sHead))
					sHead = mCell.OwningColumn.ToolTipText;
				if (string.IsNullOrEmpty(m_objGrid.Rows(iRowIndex).ErrorText)) {
					if ((dr != null)) {
						dr.RowError += "Error in Cell(s): '" + sHead + "'";
					} else {
						m_objGrid.Rows(iRowIndex).ErrorText += "Error in Cell(s): '" + sHead + "'";
					}
				} else {
					if ((dr != null)) {
						dr.RowError += ", '" + sHead + "'";
					} else {
						m_objGrid.Rows(iRowIndex).ErrorText += ", '" + sHead + "'";
					}
				}
			}
		}
	}

	private void m_objGrid_UserDeletingRow(object sender, System.Windows.Forms.DataGridViewRowCancelEventArgs e)
	{
		int iCount = m_objGrid.SelectedRows.Count;
		if (m_objGrid.NewRowIndex >= 0 && m_objGrid.SelectedRows.Contains(m_objGrid.Rows(m_objGrid.NewRowIndex))) {
			iCount -= 1;
		}
		if (!e.Cancel) {
			if (iCount > 1 || _MultiDelete) {
				if (!_MultiDelete) {
					_MultiDelete = true;
					_ConfirmDelete = Message_Delete("current selected rows");
					_RowSelectCount = iCount;
				}
				e.Cancel = !_ConfirmDelete;
				_RowSelectCount -= 1;
				if (_RowSelectCount == 0) {
					_MultiDelete = false;
				}
			} else {
				e.Cancel = !Message_Delete("current row");
			}
		}
		if ((!e.Cancel)) {
			if (Row_Deleting != null) {
				Row_Deleting(e.Row, e.Cancel);
			}
		}
	}

	private void m_txtCellEdit_Leave(object sender, System.EventArgs e)
	{
		try {
			m_txtCellEdit.Visible = false;
			m_objGrid.Enabled = true;
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	//Private Sub m_txtCellEdit_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles m_txtCellEdit.PreviewKeyDown
	private void m_txtCellEdit_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
	{
		try {
			if (e.KeyCode == Keys.Enter) {
				string sText = m_txtCellEdit.Text;
				if (m_txtCellEdit.Tag == "String") {
					ProcessMultiCellEdit(m_txtCellEdit.Text);
				} else {
					if (sText.Length == 0) {
						ProcessMultiCellEdit("");
						return;
					}
					string sFormula = null;
					decimal iValue = default(decimal);
					if (Formula_Prepare(sText, ref sFormula, ref iValue)) {
						ProcessMultiCellEdit(sFormula, iValue);
					} else {
						Message_Information("Invalid formula or value for multi cell edit." + Constants.vbCr + "Formula support is  + - * / and r (round) or o (auto order)." + Constants.vbCr + "Please contact system administrator for more information.");
					}
				}
			} else if (e.KeyCode == Keys.Escape) {
			} else if (e.KeyCode == Keys.Tab) {
			} else {
				return;
			}
			//
			//e.IsInputKey = False
			e.SuppressKeyPress = true;
			//
			m_txtCellEdit.Visible = false;
			m_objGrid.Enabled = true;
			m_objGrid.Focus();
			if ((m_Form != null))
				m_Form.HandleEsc = false;
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void ProcessMultiCellEdit(string sFormula, decimal iValue)
	{
		if (sFormula == "/" && iValue == 0)
			return;
		if (sFormula == "o")
			m_iOrder = 0;
		DataGridViewCell mCell = default(DataGridViewCell);
		//For Each mCell As DataGridViewCell In m_objGrid.SelectedCells
		for (int i = m_objGrid.SelectedCells.Count - 1; i >= 0; i += -1) {
			mCell = m_objGrid.SelectedCells(i);
			if (mCell.ReadOnly || mCell.OwningRow.IsNewRow)
				continue;
			mCell.Tag = "Edit";
			Formula_Calculate(mCell, sFormula, iValue);
			//
			RaiseEvent_CellEdit(mCell);
		}
	}

	private void RaiseEvent_CellEdit(DataGridViewCell mCell)
	{
		if (mCell.OwningColumn.HeaderCell.Tag == "DateShort") {
			ValidateDateShort(DB_GetString(mCell.Value), mCell.ColumnIndex, mCell.RowIndex);
		}
		m_objGrid.Rows(mCell.RowIndex).Tag = "Edit";
		//
		ValidateRangeInput(mCell);
		//
		if (Cell_Validated != null) {
			Cell_Validated(mCell);
		}
	}

	private void ProcessMultiCellEdit(string sValue)
	{
		foreach (DataGridViewCell mCell in m_objGrid.SelectedCells) {
			if (mCell.ReadOnly || mCell.OwningRow.IsNewRow)
				continue;
			mCell.Tag = "Edit";
			//            
			if (string.IsNullOrEmpty(sValue)) {
				mCell.Value = DBNull.Value;
			} else {
				mCell.Value = sValue;
			}
			RaiseEvent_CellEdit(mCell);
		}
	}

	private void Formula_Calculate(DataGridViewCell mCell, string sFormula, decimal iValue)
	{
		switch (sFormula) {
			case "+":
				mCell.Value = DB_GetValue(mCell.Value, 0) + iValue;
				break;
			case "-":
				mCell.Value = DB_GetValue(mCell.Value, 0) - iValue;
				break;
			case "/":
				mCell.Value = DB_GetValue(mCell.Value, 0) / iValue;
				break;
			case "*":
				mCell.Value = DB_GetValue(mCell.Value, 0) * iValue;
				break;
			case "r":
				//Round by 1,10,100,1000
				decimal tmp = DB_GetValue(mCell.Value, 0);
				tmp = tmp / iValue;
				tmp = decimal.Round(tmp, 0);
				tmp = tmp * iValue;
				mCell.Value = tmp;
				break;
			case "rh":
				//Round to five. Ex: 0.1-0.4 -> 0.5; 0.6-0.9 -> 1.0
				decimal tmp = DB_GetValue(mCell.Value, 0);
				tmp = tmp / (iValue / 0.5);
				tmp = decimal.Round(tmp, 0) * (iValue / 0.5);
				if (tmp < DB_GetValue(mCell.Value, 0)) {
					tmp += iValue;
				}
				mCell.Value = tmp;
				break;
			case "o":
				//Update order code by row position
				m_iOrder += 1;
				mCell.Value = iValue + m_iOrder;
				break;
			default:
				mCell.Value = iValue;
				break;
		}
	}

	private bool Formula_Prepare(string sText, ref string sFormula, ref decimal iValue)
	{
		if (sText.Length == 0)
			return false;
		string sValue = null;
		bool bHasPercent = false;
		sFormula = sText[0];
		sFormula = sFormula.ToLower();
		if (!"+-/*ro".Contains(sFormula)) {
			sValue = sText;
			sFormula = "";
		} else {
			if (sText[sText.Length - 1] == "%") {
				bHasPercent = true;
				sValue = sText.Substring(1, sText.Length - 2);
			} else {
				sValue = sText.Substring(1);
			}
			if (sFormula == "o" && string.IsNullOrEmpty(sValue)) {
				sValue = "0";
			}
		}
		//
		if (Information.IsNumeric(sValue)) {
			if (bHasPercent) {
				switch (sFormula) {
					case "-":
						sFormula = "*";
						iValue = 1 - (decimal.Parse(sValue) / 100);
						break;
					case "+":
						sFormula = "*";
						iValue = 1 + (decimal.Parse(sValue) / 100);
						break;
					default:
						iValue = (decimal.Parse(sValue) / 100);
						break;
				}
			} else {
				if (!decimal.TryParse(sValue, out iValue))
					return false;
				if (sFormula == "r") {
					if ("5,50,500,5000,".Contains(sValue + ",")) {
						//Round to 5
						sFormula = "rh";
					} else if (!"1,10,100,1000,".Contains(sValue + ",")) {
						return false;
					}
				}
			}
			return true;
		} else {
			return false;
		}
	}

	private decimal Formula_Evaluate(string sExp)
	{
		try {
			System.Data.DataTable table = new System.Data.DataTable();
			table.Columns.Add("expression", string.Empty.GetType(), sExp);
			DataRow row = table.NewRow();
			table.Rows.Add(row);
			return decimal.Parse(row["expression"]);
		} catch {
			return Null.NullDecimal;
		}
	}
	//
	private void ctxMenu_Click(object sender, System.EventArgs e)
	{
		switch (((ToolStripItem)sender).Name) {
			case "Filter_Edit":
				if (m_dicFilterColumn == null)
					return;
				foreach (DataRow dr in m_SourceTable.GetErrors()) {
					if (dr.RowState != DataRowState.Deleted) {
						Message_Information("Please correct all rows error before filter data");
						return;
					}
				}

				//
				frmGridFilter f = new frmGridFilter();
				DataGridViewRow mRow = default(DataGridViewRow);
				f.Column_Name.DisplayMember = "DisplayField";
				f.Column_Name.ValueMember = "ValueField";
				foreach (string sKey in m_dicFilterColumn.Keys) {
					ListBoxItem itm = new ListBoxItem(sKey, m_dicFilterColumn[sKey]);
					f.Column_Name.Items.Add(itm);
				}

				//
				DataTable dt = m_SourceTable.Clone();
				dt.DefaultView.RowFilter = m_SourceTable.DefaultView.RowFilter;
				if (f.Show_Form(dt)) {
					frmProgress.Instance.ThreadSub = Filter_Apply;
					frmProgress.Instance.ThreadInputObject = f.Tag;
					frmProgress.Instance.Show_Progress("Please wait ...");
				}
				f.Close();
				break;
			case "Filter_Clear":
				frmProgress.Instance.ThreadSub = Filter_Apply;
				frmProgress.Instance.ThreadInputObject = "";
				frmProgress.Instance.Show_Progress("Please wait ...");
				break;
		}
	}

	private void Filter_Apply(object sFilter)
	{
		if (sFilter == null)
			sFilter = "";
		if ((m_objGrid.CurrentRow != null) && m_objGrid.CurrentRow.IsNewRow) {
			m_objGrid.CancelEdit();
		}
		m_SourceTable.DefaultView.RowFilter = sFilter;
		if ((m_objGrid.CurrentRow != null)) {
			m_objGrid.InvalidateRow(m_objGrid.CurrentRow.Index);
		}
		m_objGrid.Refresh();
		if (string.IsNullOrEmpty(m_SourceTable.DefaultView.RowFilter)) {
			m_objGrid.TopLeftHeaderCell.Style.BackColor = null;
		} else {
			m_objGrid.TopLeftHeaderCell.Style.BackColor = Color.LightGoldenrodYellow;
		}
	}

	private void m_objGrid_ReadOnlyChanged(object sender, System.EventArgs e)
	{
		if (m_objGrid.ReadOnly) {
			m_objGrid.TopLeftHeaderCell.ContextMenuStrip = null;
		} else {
			m_objGrid.TopLeftHeaderCell.ContextMenuStrip = mnuContext;
		}
	}

	private void m_objGrid_CellErrorTextChanged(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			DataGridViewCell mCell = m_objGrid.Item(e.ColumnIndex, e.RowIndex);
			if (mCell.OwningRow.DataBoundItem is DataRowView) {
				if (!string.IsNullOrEmpty(mCell.OwningColumn.DataPropertyName)) {
					((DataRowView)mCell.OwningRow.DataBoundItem).Row.SetColumnError(mCell.OwningColumn.DataPropertyName, mCell.ErrorText);
				}
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_RowErrorTextChanged(object sender, System.Windows.Forms.DataGridViewRowEventArgs e)
	{
		if ((m_SourceTable != null) && e.Row.Index < m_SourceTable.DefaultView.Count) {
			((DataRowView)e.Row.DataBoundItem).Row.RowError = e.Row.ErrorText;
		}
	}

	private void m_objGrid_EditingControlShowing(object sender, System.Windows.Forms.DataGridViewEditingControlShowingEventArgs e)
	{
		if (e.Control is ComboBox) {
			((ComboBox)e.Control).DropDownStyle = ComboBoxStyle.DropDown;
		//DirectCast(e.Control, ComboBox).DroppedDown = True
		} else if (e.Control is TextBox) {
			if (m_objGrid.CurrentCell.ValueType.Name == "DateTime" && !Information.IsDBNull(m_objGrid.CurrentCell.Value)) {
				switch (m_objGrid.CurrentCell.OwningColumn.DefaultCellStyle.Format) {
					case CS_DISPLAY_DATE_FORMAT:
						m_objGrid.EditingControl.Text = ((DateTime)m_objGrid.CurrentCell.Value).ToString(CS_EDIT_DATE_FORMAT);
						break;
					case CS_DISPLAY_TIME_FORMAT:
						m_objGrid.EditingControl.Text = ((DateTime)m_objGrid.CurrentCell.Value).ToString(CS_EDIT_TIME_FORMAT);
						break;
				}
			}
			//
			if (m_dicCaseInput.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name)) {
				((TextBox)e.Control).CharacterCasing = m_dicCaseInput[m_objGrid.CurrentCell.OwningColumn.Name];
			} else {
				((TextBox)e.Control).CharacterCasing = CharacterCasing.Normal;
			}
			//
			int iMaxLength = 0;
			if (m_SourceTable != null && m_objGrid.CurrentCell.OwningColumn.DataPropertyName != null && m_SourceTable.Columns.Contains(m_objGrid.CurrentCell.OwningColumn.DataPropertyName)) {
				iMaxLength = m_SourceTable.Columns[m_objGrid.CurrentCell.OwningColumn.DataPropertyName].MaxLength;
			} else if (m_dicMaxInput != null && m_dicMaxInput.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name)) {
				iMaxLength = m_dicMaxInput[m_objGrid.CurrentCell.OwningColumn.Name];
			}
			if (iMaxLength < 0)
				iMaxLength = 0;
			((TextBox)e.Control).MaxLength = iMaxLength;
			//
			if (e.Control.Tag == null) {
				e.Control.Tag = "HANDLER";
				((TextBox)e.Control).PreviewKeyDown += m_objGrid_PreviewKeyDown;
			}
		}
	}
	private bool _isCellChanging;
	private void m_objGrid_CellValueChanged(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			if (e.RowIndex < 0 || e.ColumnIndex < 0)
				return;
			if (e.RowIndex > m_objGrid.RowCount - 1)
				return;
			//
			m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex).ErrorText = "";
			if (!_isLookupInvoke) {
				if (m_dicLookupInvoker.ContainsKey(m_objGrid.Columns(e.ColumnIndex).Name)) {
					Cell_Lookup(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex), LookupMode.CheckData);

				} else {
					if (_isCellChanging)
						return;
					_isCellChanging = true;
					RaiseEvent_CellEdit(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex));
					if (m_lstTimeEx.Contains(m_objGrid.Columns(e.ColumnIndex).Name)) {
						foreach (object columnName_loopVariable in m_dicTimeEx.Keys) {
							columnName = columnName_loopVariable;
							if (m_dicTimeEx[columnName].ToString() == m_objGrid.Columns(e.ColumnIndex).Name) {
								m_objGrid.Rows(e.RowIndex).Cells(m_dicTimeEx[columnName].ToString()).Value = Time_ParseEx(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex).Value, m_objGrid.Rows(e.RowIndex).Cells(m_dicTimeEx[columnName].ToString()).FormattedValue);
							}
						}
					}
					_isCellChanging = false;
				}
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_CellDoubleClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			if (e.RowIndex >= 0 && (m_DbClickMethod != null)) {
				m_DbClickMethod.Invoke();
			}
			if (m_objGrid.ReadOnly)
				m_objGrid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private int _prevRowIndex = -1;
	private void m_objGrid_SelectionChanged(object sender, System.EventArgs e)
	{
		if (m_IsLoading)
			return;
		//
		if (m_objGrid.SelectedRows.Count > 0 && (m_objGrid.CurrentRow == null || m_objGrid.SelectedRows(0).Index != m_objGrid.CurrentRow.Index)) {
			foreach (DataGridViewCell cell in m_objGrid.SelectedRows(0).Cells) {
				if (cell.Visible) {
					m_objGrid.CurrentCell = cell;
					break; // TODO: might not be correct. Was : Exit For
				}
			}
		}
		if ((m_objGrid.Visible & (m_objGrid.CurrentRow != null)) && m_objGrid.CurrentRow.Index != _prevRowIndex && (m_SelectMethod != null)) {
			_prevRowIndex = m_objGrid.CurrentRow.Index;
			m_SelectMethod.Invoke();
		}
	}

	private void m_objGrid_CellFormatting(object sender, System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
	{
		try {
			if (e.ColumnIndex < 0 || e.RowIndex < 0)
				return;
			if (m_objGrid.Columns(e.ColumnIndex).ReadOnly && !m_objGrid.ReadOnly) {
				e.CellStyle.BackColor = App_GetColor(CS_STYLE_VIEW);
				//SystemColors.Control
				e.CellStyle.ForeColor = SystemColors.ControlText;
			}
			//Apply format setting by value
			if ((m_dicFormatColumn != null) && m_dicFormatColumn.ContainsKey(GetColumnKey(e.ColumnIndex).ToUpper())) {
				Dictionary<object, string> formatDic = m_dicFormatColumn[GetColumnKey(e.ColumnIndex).ToUpper()];
				if ((DB_GetValue(e.Value, null) != null) && formatDic.ContainsKey(e.Value)) {
					string[] arrColor = formatDic[e.Value].Split(";");
					e.CellStyle.BackColor = App_GetColor(arrColor[0]);
					if (arrColor.Length > 1)
						e.CellStyle.BackColor = App_GetColor(arrColor[1]);
				}
			}
			//
			if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewImageColumn)
				return;
			//
			object oValue = DB_GetValue(e.Value);
			string columnName = m_objGrid.Columns(e.ColumnIndex).Name;
			//Apply time ex format
			if (!string.IsNullOrEmpty(oValue.ToString()) && m_dicTimeEx != null && m_dicTimeEx.ContainsKey(columnName)) {
				if (m_dicTimeEx[columnName] is System.DateTime) {
					e.Value = Time_ToStringEx(m_dicTimeEx[columnName], oValue);
				} else {
					e.Value = Time_ToStringEx(GetRowData(e.RowIndex)[m_dicTimeEx[columnName].ToString()], oValue);
				}
			}
			//Apply row change format
			if (m_lstCellChangeOld != null) {
				string cellID = string.Format("{0}@{1}", e.RowIndex, e.ColumnIndex);
				if (m_lstCellChangeOld.Contains(cellID)) {
					e.CellStyle.BackColor = m_bgrOldChange;
				} else if (m_lstCellChangeNew.Contains(cellID)) {
					e.CellStyle.BackColor = m_bgrNewChange;
				}
			}
			//
			if (Cell_Format != null) {
				Cell_Format(m_objGrid.Columns(e.ColumnIndex).Name, e);
			}
			e.FormattingApplied = oValue.GetType().Name != DB_GetValue(e.Value).GetType.Name || (oValue != DB_GetValue(e.Value));
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_CellContentClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			if (m_objGrid.Columns(e.ColumnIndex).Name.EndsWith("_clmCalendar")) {
				btnCalendar_Click(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex - 1));
			} else if (m_objGrid.Columns(e.ColumnIndex).Name.EndsWith("_clmLookup")) {
				string columnName = m_objGrid.Columns(e.ColumnIndex).Name;
				columnName = columnName.Substring(0, columnName.Length - "_clmLookup".Length);
				if (m_objGrid.Rows(e.RowIndex).Cells(columnName).ReadOnly == false) {
					Cell_Lookup(m_objGrid.Rows(e.RowIndex).Cells(columnName), LookupMode.SelectData);
				}
			} else if (m_objGrid.Columns(e.ColumnIndex).Name == cs_clmDeleteRow) {
				//m_objGrid.Rows.RemoveAt(e.RowIndex)
				if (e.RowIndex != m_objGrid.NewRowIndex) {
					m_objGrid.ClearSelection();
					m_objGrid.Rows(e.RowIndex).Selected = true;
					//SendKeys.SendWait("{DEL}")
					if (Message_Delete("current row")) {
						bool eCancel = false;
						if (Row_Deleting != null) {
							Row_Deleting(m_objGrid.Rows(e.RowIndex), eCancel);
						}
						if (!eCancel)
							GetRowData(e.RowIndex).Delete();
					}
				}
			} else if (m_objGrid.Columns(e.ColumnIndex) is DataGridViewButtonColumn) {
				if (m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex) is DataGridViewDisableButtonCell && !((DataGridViewDisableButtonCell)m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex)).Enabled) {
					return;
					//do nothing if cell disabled
				}
				if (CellButton_Click != null) {
					CellButton_Click(m_objGrid.Rows(e.RowIndex).Cells(e.ColumnIndex));
				}
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void btnCalendar_Click(DataGridViewCell cell)
	{
		System.Windows.Forms.Form objForm = m_objGrid.FindForm();
		MonthCalendar calendarPopup = objForm.Controls["calendarGrid"];
		if (calendarPopup == null) {
			calendarPopup = new MonthCalendar();
			calendarPopup.Name = "calendarGrid";
			objForm.Controls.Add(calendarPopup);
			calendarPopup.Visible = false;
			calendarPopup.MaxSelectionCount = 1;
			calendarPopup.BackColor = Color.AliceBlue;
			//
			calendarPopup.DateSelected += Calendar_DateChanged;
			calendarPopup.Leave += Calendar_Leave;
		}
		//
		DateTime dValue = default(DateTime);
		if (Date_TryParseEx(cell.FormattedValue, dValue)) {
			calendarPopup.SetDate(dValue);
		}
		//
		Point locationOnForm = GetCellLocation(cell);
		//New Point(GetPosX(cell.ColumnIndex), GetPosY(cell.RowIndex))
		//
		calendarPopup.Location = new Point(locationOnForm.X + 1, locationOnForm.Y + cell.Size.Height + 1);
		if (calendarPopup.Left + calendarPopup.Width > objForm.Width - 6) {
			calendarPopup.Left = locationOnForm.X - (calendarPopup.Width - cell.Size.Width);
		}
		if (calendarPopup.Top + calendarPopup.Height > objForm.Height - 6) {
			calendarPopup.Top = locationOnForm.Y - (calendarPopup.Height);
		}
		calendarPopup.Visible = true;
		calendarPopup.BringToFront();
		calendarPopup.Focus();
		calendarPopup.Tag = cell;
	}

	private void Calendar_DateChanged(object sender, System.Windows.Forms.DateRangeEventArgs e)
	{
		DataGridViewCell cell = (DataGridViewCell)sender.Tag;
		cell.DataGridView.CurrentCell = cell;
		cell.DataGridView.BeginEdit(false);
		((Control)sender).Visible = false;
		m_objGrid.EditingControl.Text = e.Start.ToString(CS_EDIT_DATE_FORMAT);
		m_objGrid.CurrentCell.Value = e.Start;
	}

	private void Calendar_Leave(object sender, System.EventArgs e)
	{
		((Control)sender).Visible = false;
	}

	private void m_objGrid_CellEnter(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		try {
			if (m_dicLookupInit.ContainsKey(m_objGrid.Columns(e.ColumnIndex).Name)) {
				if ((m_Form != null))
					m_Form.HandleLookup = true;
			}
		} catch {
		}
	}

	private void m_objGrid_CellMouseLeave(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		if ((m_Form != null))
			m_Form.HandleLookup = false;
	}

	private void m_objGrid_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e)
	{
		try {
			if (e.KeyData == CS_LOOKUP_KEY && m_dicLookupInit.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name)) {
				Cell_Lookup(m_objGrid.CurrentCell, LookupMode.SelectData);
			}
		} catch (Exception ex) {
			Message_Error(ex);
		}
	}

	private void m_objGrid_RowEnter(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
	{
		//If Not m_SelectMethod Is Nothing Then
		//    m_SelectMethod.Invoke()
		//End If
	}

	#region "IDisposable Support"
		// To detect redundant calls
	private bool disposedValue;

	// IDisposable
	protected virtual void Dispose(bool disposing)
	{
		if (!this.disposedValue) {
			if (disposing) {
				// TODO: dispose managed state (managed objects).
				if (m_timerChange != null) {
					m_timerChange.Enabled = false;
					m_timerChange.Dispose();
				}
			}
			// TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
			// TODO: set large fields to null.
		}
		this.disposedValue = true;
	}

	// TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
	//Protected Overrides Sub Finalize()
	//    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
	//    Dispose(False)
	//    MyBase.Finalize()
	//End Sub

	// This code added by Visual Basic to correctly implement the disposable pattern.
	public void Dispose()
	{
		// Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
		Dispose(true);
		GC.SuppressFinalize(this);
	}
	#endregion

}

internal class MergeColumnHeaderInfo
{
	//Cấu trúc để chứa thông tin merge header
		//Tên cột bắt đầu
	public string FromName;
		//So cot merge
	public int ColSpan;
		//Index của cột bắt đầu (tự động tính)
	public int FromIndex;
		//Index của cột kết thúc (tự động tính)
	public int ToIndex;
		//Nhãn của merge header
	public string Caption;
		// Độ rộng (tự động tính)
	public int Width;
		// Yes: phủ toàn bộ các header; No: phủ 1/2 và giữ lại text của header
	public bool Cover;
}

public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn
{

	public DataGridViewDisableButtonColumn()
	{
		this.CellTemplate = new DataGridViewDisableButtonCell();
	}
}

public class DataGridViewDisableButtonCell : DataGridViewButtonCell
{

	private bool enabledValue;
	public bool Enabled {
		get { return enabledValue; }
		set { enabledValue = value; }
	}

	// Override the Clone method so that the Enabled property is copied. 
	public override object Clone()
	{
		DataGridViewDisableButtonCell Cell = (DataGridViewDisableButtonCell)base.Clone();
		Cell.Enabled = this.Enabled;
		return Cell;
	}

	// By default, enable the button cell. 
	public DataGridViewDisableButtonCell()
	{
		this.enabledValue = true;
	}

	protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,

	DataGridViewPaintParts paintParts)
	{
		// The button cell is disabled, so paint the border,   
		// background, and disabled button for the cell. 

		if (!this.enabledValue) {
			// Draw the background of the cell, if specified. 

			if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background) {
				SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor);
				graphics.FillRectangle(cellBackground, cellBounds);
				cellBackground.Dispose();
			}

			// Draw the cell borders, if specified. 

			if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border) {
				PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
			}

			// Calculate the area in which to draw the button. 
			Rectangle buttonArea = cellBounds;
			Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle);
			buttonArea.X += buttonAdjustment.X;
			buttonArea.Y += buttonAdjustment.Y;
			buttonArea.Height -= buttonAdjustment.Height;
			buttonArea.Width -= buttonAdjustment.Width;

			// Draw the disabled button.                
			ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled);

			// Draw the disabled button text.  
			if (this.FormattedValue is string) {
				TextRenderer.DrawText(graphics, Convert.ToString(this.FormattedValue), this.DataGridView.Font, buttonArea, SystemColors.GrayText);
			}

		} else {
			// The button cell is enabled, so let the base class  
			// handle the painting. 
			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle,
			paintParts);
		}
	}

}
